Strange mouse movement and repaint problems in AU

hi zamrate,

could you send me/share your changes on the timer cause i have some Timer related problems on the mac and i 'd like to check if your changes would solve them as well.

I am experiencing problems with the standalone on mac. everything works ok in the plugins (AU and VST ).
I use messages between audio/UI to set the parameters and the more i use timers in the standalone the more the messaging and repaint are delayed, then the messages piles up before being delivered.

There is also a link with the fact the mouse button is down or not. It seems ok when i set parameters for a midi controller but if i move a (large) component then i have the delay problem.

I have been following the talks about the highlighting prob but strangely it’s fine in the plugins but not as an apllication.

I am running out of ideas. :?

any idea guys?

cheers

Send it. Good luck! Tell us if it works!

The timer class only uses a single message to service all the timers, no matter how many you have, so I’d suspect this is more to do with you performing a lot of work in your timer callbacks, or maybe creating new timer objects every time the mouse moves, or similar kind of thing. You might be getting away with it in a plugin because the way timers are called back is slightly different.

Hi Jules,

i use messages to update the VU meter and have the same problem like
zamrate. In some cases, it seems that the timer in the AU-Wrapper is blocking the whole messaging system. Would it be a solution to install a separate (independent) carbon based timer, to update the mouse move events for AU? What do you think?

Even the OS X Juce AU Demo, non-modified, does not work when using the original Juce Timer class: The keys of the keyboard won’t always highlight when you pass over them with the mouse. With the modified Timer, it works. I told you that weeks ago :slight_smile:

Hi Zamrate,

I am glad that you solve this problem. I have the same problems with AU and Logic for a long time. Can you please post your solution?

Thanks

fyi, it helped me to reduce the refreshing rate of the VU-Meter. i will measure the time since the last refresh, and skip the refresh it if its to small (for example <100 ms)

These timers are driving me mad at the moment. I’m trying to come up with a neat way of working your suggestions into the code, but it’s annoying that the Mac has a great native timer implementation, but there’s no equivalent on windows or linux.

Could it be that on Mac OS X, it’s the function to read out the microseconds (don’t remember the name right now) you’re using that causes the trouble?

Ok, here’s my modified source code anyway, OS X only:
EDIT: MADE A MODIFICATION IN juce_mac_timers.cpp on 15th may 2008. MAKE SURE TO USE THIS VERSION.

JUCE_Timer.cpp

/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-7 by Raw Material Software ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the
   GNU General Public License, as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later version.

   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.

   You should have received a copy of the GNU General Public License
   along with JUCE; if not, visit www.gnu.org/licenses or write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
   Boston, MA 02111-1307 USA

  ------------------------------------------------------------------------------

   If you'd like to release a closed-source product which uses JUCE, commercial
   licenses are also available: visit www.rawmaterialsoftware.com/juce for
   more information.

  ==============================================================================
*/

#include "../../juce_core/basics/juce_StandardHeader.h"

#include "../../../build/macosx/platform_specific_code/juce_mac_Timers.h"

BEGIN_JUCE_NAMESPACE

void mac_startTimer(void *timer, int milliseconds);
void mac_stopTimer(void *timer);

#include "juce_Timer.h"
#include "juce_MessageManager.h"
#include "juce_AsyncUpdater.h"
#include "../application/juce_Application.h"
#include "../application/juce_DeletedAtShutdown.h"
#include "../../juce_core/basics/juce_Time.h"
#include "../../juce_core/threads/juce_Thread.h"
#include "../../juce_core/threads/juce_ScopedLock.h"
#include "../../juce_core/containers/juce_VoidArray.h"



Timer::Timer() throw()
   : countdownMs (0),
     periodMs (0),
     previous (0),
     next (0)
{
#ifdef JUCE_DEBUG
    activeTimers.add (this);
#endif
}


Timer::Timer (const Timer&) throw()
   : countdownMs (0),
     periodMs (0),
     previous (0),
     next (0)
{
	
}

Timer::~Timer()
{
    stopTimer();
}

void Timer::startTimer (const int interval) throw()
{
	stopTimer();
	periodMs=interval;
	if (interval>0) mac_startTimer((void*)this,interval);
}

void Timer::stopTimer() throw()
{
	if (periodMs>0) mac_stopTimer(this);
	periodMs=0;
}



END_JUCE_NAMESPACE

Juce_Timer.h

/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-7 by Raw Material Software ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the
   GNU General Public License, as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later version.

   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.

   You should have received a copy of the GNU General Public License
   along with JUCE; if not, visit www.gnu.org/licenses or write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
   Boston, MA 02111-1307 USA

  ------------------------------------------------------------------------------

   If you'd like to release a closed-source product which uses JUCE, commercial
   licenses are also available: visit www.rawmaterialsoftware.com/juce for
   more information.

  ==============================================================================
*/


#ifndef __JUCE_TIMER_JUCEHEADER__
#define __JUCE_TIMER_JUCEHEADER__



class InternalTimerThread;


//==============================================================================
/**
    Repeatedly calls a user-defined method at a specified time interval.

    A Timer's timerCallback() method will be repeatedly called at a given
    interval. Initially when a Timer object is created, they will do nothing
    until the startTimer() method is called, then the message thread will
    start calling it back until stopTimer() is called.

    The time interval isn't guaranteed to be precise to any more than maybe
    10-20ms, and the intervals may end up being much longer than requested if the
    system is busy. Because it's the message thread that is doing the callbacks,
    any messages that take a significant amount of time to process will block
    all the timers for that period.

    If you need to have a single callback that is shared by multiple timers with
    different frequencies, then the MultiTimer class allows you to do that - its
    structure is very similar to the Timer class, but contains multiple timers
    internally, each one identified by an ID number.

    @see MultiTimer
*/
class JUCE_API  Timer
{
protected:
    //==============================================================================
    /** Creates a Timer.

        When created, the timer is stopped, so use startTimer() to get it going.
    */
    Timer() throw();

    /** Creates a copy of another timer.

        Note that this timer won't be started, even if the one you're copying
        is running.
    */
    Timer (const Timer& other) throw();

public:
    //==============================================================================
    /** Destructor. */
    virtual ~Timer();

    //==============================================================================
    /** The user-defined callback routine that actually gets called periodically.

        It's perfectly ok to call startTimer() or stopTimer() from within this
        callback to change the subsequent intervals.
    */
    virtual void timerCallback() = 0;

    //==============================================================================
    /** Starts the timer and sets the length of interval required.

        If the timer is already started, this will reset it, 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 (const int intervalInMilliseconds) throw();

    /** Stops the timer.

        No more callbacks will be made after this method returns.

        If this is called from a different thread, any callbacks that may
        be currently executing may be allowed to finish before the method
        returns.
    */
    void stopTimer() throw();

    //==============================================================================
    /** Checks if the timer has been started.

        @returns true if the timer is running.
    */
    bool isTimerRunning() const throw()                     { return periodMs > 0; }

    /** 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 throw()                    { return periodMs; }


    //==============================================================================
	void* OSXEventLoopTimerRef;
	void* OSXEventLoopTimerUPP;
private:
    friend class InternalTimerThread;
    int countdownMs, periodMs;
    Timer* previous;
    Timer* next;
	
	
    const Timer& operator= (const Timer&);

};

#endif   // __JUCE_TIMER_JUCEHEADER__

juce_mac_timers.cpp

#include <Carbon/Carbon.h>
#include "../../../src/juce_core/basics/juce_StandardHeader.h"

BEGIN_JUCE_NAMESPACE

#include "../../../src/juce_appframework/events/juce_Timer.h"



OSStatus timerProc(EventLoopTimerRef inTimer, void *inUserData) // inUserData is the pointer to our Timer object
{
	((Timer*)inUserData)->timerCallback();
	return noErr;
}

void mac_startTimer(void *timer_, int milliseconds)
{
	Timer *timer=(Timer*)timer_;
	
	EventLoopTimerRef ref;
	EventLoopTimerUPP timerUPP = NewEventLoopTimerUPP((EventLoopTimerProcPtr)timerProc);
	InstallEventLoopTimer(GetMainEventLoop(),kEventDurationMillisecond*milliseconds,kEventDurationMillisecond*milliseconds,timerUPP,(void*)timer,&ref);
	
	timer->OSXEventLoopTimerRef=(void*)ref;
	timer->OSXEventLoopTimerUPP=(void*)timerUPP;
}

void mac_stopTimer(void *timer_)
{
	Timer *timer=(Timer*)timer_;
    EventLoopTimerUPP timerUPP=(EventLoopTimerUPP)timer->OSXEventLoopTimerUPP;
	EventLoopTimerRef ref=(EventLoopTimerRef)timer->OSXEventLoopTimerRef;
	
	RemoveEventLoopTimer(ref);
	DisposeEventLoopTimerUPP(timerUPP);
}


END_JUCE_NAMESPACE

juce_mac_timers.h

void mac_startTimer(void *timer, int milliseconds);
void mac_stopTimer(void *timer);

there is a low level timer implementation in linux. look at this code:

     #include <stdio.h>
     #include <string.h>
     #include <signal.h>
     #include <sys/time.h>

     // This is a timer handler.
     void timerHandler (int signum)
     {
        static int counter= 0;
        printf ("timerHandler: counter= %d\n", counter++);
        fflush (stdout);
     }

     // This is the one and only main function.
     int main (void)
     {
        struct sigaction sa;
        struct itimerval timer;

        // Install the timer handler...
        memset (&sa, 0, sizeof (sa));
        sa.sa_handler= &timerHandler;
        sigaction (SIGALRM, &sa, NULL);

        // Configure the timer to expire every 100 msec...
        timer.it_value.tv_sec= 0;
        timer.it_value.tv_usec= 100000;
        timer.it_interval.tv_sec= 0;           // Interval
        timer.it_interval.tv_usec= 100000;

        // Start timer...
        setitimer (ITIMER_REAL, &timer, NULL);

        // Do noting...
        while (1)
           sleep (10);
    }

you can test it with:

gcc -o timer timer.c 

it works pretty well and don’t require X11 to be present (ie usable with JUCE_BUILD_GUI_CLASSES set to false). The only drawback is having more than one timer with different expire time.

Yes, thanks. Implementing it isn’t a problem though - I want to understand why the current one is failing.

The only difference between the native/non-native timers is that the native ones would be able to jump the message queue and get themselves delivered before any pending messages, whereas the juce method can only post a message that goes on the end of the queue.

That produces slightly different behaviour characteristics, and the mac version is preferable, because the timers should be more accurate under heavy load. But there’s no way of duplicating the same thing on windows/linux, which is why I’m reluctant to change the way just one platform works.

I could improve the current version by hacking the juce event loop so that it services any timers each time any message is delivered, but even that wouldn’t work in a plugin, because the host is in charge of the event loop…

Doesn’t seem at all likely to me.

Are Juce’s non-native-Timer messages really delivered at all? If they would get delivered (even if too late) my UV meters, should work with the Juce (non native) based Timer, but they stop working completely and forever.

Yes, of course they’re delivered. The fact that they stop arriving means that something has stopped that timer thread from running, and that’s one of the things I want to figure out. Could be that the host actually just kills any threads when a plugin is removed, without letting them exit gracefully.

Also remember, that until I changed the Timer class, the automation recording wasn’t working in Logic with any JUCE-based AU plugin. Perhaps that could be a hint.

That’s the puzzling bit. I can’t think of any timers that are needed for automation to work.

Maybe it’s just something screwy about logic’s event loop, and having any timer at all sorts it out? It’s maybe worth trying it with the original juce code, but in your plugin, create a dummy Mac timer that just keeps firing and doing nothing, and see if that has any effect.

Sorry, I don’t have the time to do this at the moment, I’m too busy. I’m sure you can get Logic somewhere (even if it’s a trial version) and you can try out with the unmodified Juce Demo AU or any other JUCE based plugin: Recording automation just doesn’t work, unless you modify the Timer class!

Same thing in Digital Performer (reported by users). I’m sure you will find out what it is, because you’re by far a better programmer than I am, and also I don’t have any expertise with Mac’s so… :wink:

I do actually have a logic NFR dongle around here somewhere…

I want to remind you that using gestures makes automation work in Logic. This should definitely be added to the Demo AU code. No timer modification required (but perhaps for the other problems you had).

I can second that.

I’m on Intel, Logic 8.0.1, Mac OS X.4.11, JUCE SVN rev 407.

No Timer mods but I am using beginParameterChangeGesture/endParameterChangeGesture.