Quick Sign In:  

Forum: VirtualDJ Plugins

Topic: Best Way to Move a Slider?
I would like to be able to smoothly increment a slider with my plugin.

I initially thought to use std::thread to create asynchronous timers to send commands periodically over a number of milliseconds. While I can asynchronously move two sliders at the same time this way, I haven't figured out how to convince VDJ to keep playing the music while the sliders are moving.

Before I get too invested in trying to get this to work, I thought I might check to see if I am even on the right track with std::thread, or whether there is a simpler, more elegant way to move a slider over x milliseconds.

What would be the best way to move a slider smoothly over a period of time with a plugin?
 

Posted Mon 23 Oct 23 @ 12:17 am
locoDogPRO InfinityModeratorMember since 2013
std::thread is the way I've been shown.
 

Posted Mon 23 Oct 23 @ 3:42 am
locodog wrote :
std::thread is the way I've been shown.


Thanks.

This is the code that I tried:

class Timer {
public:
Timer(size_t time, const std::function<void(void)>& f) : time{ std::chrono::milliseconds{time} }, f{ f } {}
~Timer() { wait_thread.join(); }

private:
void wait_then_call()
{
std::unique_lock<std::mutex> lck{ mtx };
for (int i{ 10 }; i > 0; --i) {
f();
cv.wait_for(lck, time / 10);
}
}
std::mutex mtx;
std::condition_variable cv{};
std::chrono::milliseconds time;
std::function <void(void)> f;
std::thread wait_thread{ [this]() {wait_then_call(); } };
};


I called it with these functions:

void CvdjMixmaster::volumeTimerFunc(size_t time)
{
auto f = [&]() {incrementVolume(); };
Timer volumeTimer{ time,f };
}

void CvdjMixmaster::lowsTimerFunc(size_t time)
{
auto f = [&]() {incrementLows(); };
Timer lowsTimer{ time,f };
}


Both timers will run concurrently with each other, but VDJ stops the music until the timers have completed.

Do you have a code sample of doing something like that without interrupting the music?
 

Posted Mon 23 Oct 23 @ 7:10 am
AdionPRO InfinityCTOMember since 2006
You create the Timer object inside of your volumeTimerFunc, but the destructor of the Timer explicitly waits for the thread to end.
So the volumeTimerFunc will effectively wait until the ramp is completed, making it effectively useless to use a different thread.
The timers you want to use should probably be members of your CvdjMixmaster class, so that they aren't destroyed while still running, and use a function to start the thread instead of starting it when the timer is created.
Also not sure what the mutex is for in this case, since it's only used in the thread itself.

Finally, although probably ok for just 2 timers, you might want to have just a single thread that handles all your timers. Creating and destroying a new thread every time you want to ramp a slider is a little bit wasteful.
 

Posted Mon 23 Oct 23 @ 7:18 am
Adion wrote :
...


Thanks so much for your help!
 

Posted Mon 23 Oct 23 @ 8:00 pm
I put this function in my CvdjMixmaster class:

void CvdjMixmaster::volumeTimerFunc()
{
long interval = m_executionTime/100;
auto nowTime = std::chrono::system_clock::now();
std::time_t sleepTime = std::chrono::system_clock::to_time_t(nowTime);
tm myLocalTime = *localtime(&sleepTime);
for (int i{ 100 }; i > 0; --i) {
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
HRESULT hr;
hr = SendCommand("deck default volume +1%");
}
}


And called it like this:

std::thread runThisAsync{ &CvdjMixmaster::volumeTimerFunc, this };


It worked great, except that it gave the following debug error in VDJ:



If I click retry, the music plays while my slider goes up though. Small victories.

How can I keep abort() from being called?
 

Posted Mon 23 Oct 23 @ 9:50 pm
AdionPRO InfinityCTOMember since 2006
The runThisThread var is now created in the function where you call it, so it will be destroyed when that function completes, but with the thread still running.
The std::thread option should be a member of your class so that it's not destroyed immediately.

For debugging, if you set up debugging within visual studio, you can start vdj from within visual studio. Then when you press retry visual studio will stop exactly where your plugin crashed.
 

Posted Tue 24 Oct 23 @ 3:37 am
Adion wrote :
The runThisThread var is now created in the function where you call it, so it will be destroyed when that function completes, but with the thread still running.
The std::thread option should be a member of your class so that it's not destroyed immediately.

For debugging, if you set up debugging within visual studio, you can start vdj from within visual studio. Then when you press retry visual studio will stop exactly where your plugin crashed.


Thanks! It's working great now.
 

Posted Wed 25 Oct 23 @ 7:39 pm