Ingreso rápido:  

Forum: VirtualDJ Technical Support

Tema: HID controller - definition and mapping for motorised jogwheel
Hi!

I'm developing a motorised controller and have some questions on how to best control the platter.

I have a working prototype HID implementation. I can get values to and from VDJ. I can control the track movement with the jogwheel. The definition and mapping for that looks like this:

definition:
 
<page type="in">
<jog byte="1" size="word" full="65535" name="JOGWHEEL" deck="1" />
...

mapping:
<map value="JOGWHEEL" action="jogwheel" />


The jogwheel is motorised and with this configuration the sound plays almost correctly but not fully when I run the platter. It's a bit glitchy, small tempo jerks here and there.

So this is not ideal and I see there's some better candidates in the API:
motorwheel and speedwheel

For motorwheel I have to run
motorwheel "move" +1 
followed by
motorwheel "timestamp" 1000.0


It's not clear how I can get the the jogwheel data from the definition into those functions.
Similarly for the speedwheel. I have to provide 2 parameters. It's not clear how I make the link from the out page in the definition to variables in the mapping/scripts.

Can anyone provide an example on how to do that?

Other questions:
How often does VDJ read the HID reports? i.e. how often should I ideally create new reports with the platter position.

How does the <action> element in the controller definition work? Can you give an example? I have a feeling that might solve my question above.

Thanks!
Beau
 

Mensajes Sun 29 Jan 23 @ 9:34 pm
AdionPRO InfinityCTOMember since 2006
Add motor="true" to <device ... motor="true" />

For the mapping, you could use
motorwheel3 posdelta
and
motorwheel3 timedelta 1000

To get the data to these 2 from hid, you could use <jog> as you've done 2 times, one for the position and one for the time.

Your controller should have in the hid reports a number counting up when moving forward and counting down when moving backward, wrapping around at the boundaries defined in the <jog /> definition
The definition defines full= as one rotation.

Your controller should also have a counter that counts based on time, always counting up at a fixed speed regardless of what the jog is doing. For most microcontrollers I think there will probably be such a timer based on the clock speed that could be used and scaled.
The '1000' in the mapping is the number of counts per millisecond for the time counter. (currently it assume full=16384 so you may need to use a different value depending on your counter.
 

Mensajes Sun 29 Jan 23 @ 10:18 pm
Thanks for the quick reply.

I added the time counter and I am able to move the track, but not correctly yet.

I don't fully get the scaling. A value of 16384 in the report page should correspond to a time of 1000 microseconds / 1millisecond? and it's the time delta since the last report?
 

Mensajes Mon 30 Jan 23 @ 9:24 am
AdionPRO InfinityCTOMember since 2006
Your hid report should just count, calculating the delta between reports is done by vdj
 

Mensajes Mon 30 Jan 23 @ 10:21 am
Still missing something.. it runs now but way to fast.

I'm sending this value in the counter:
    
uint64_t time = esp_timer_get_time(); //microseconds
uint16_t time_vdj = (uint16_t)((time * 16384 / 1000)) ; //'full' 16384 = 1000us


I use the same timer to send the position of a dummy platter for testing:
uint16_t position = int(0.049152 * float(time)); //45pm=0.75rev/s 1s='full' 2^16 -> seems to give correct speed for 'jogwheel'


definition has:

<jog byte="1" size="word" full="65535" name="POSDELTA" deck="1" />
<jog byte="3" size="word" full="16384" name="TIMEDELTA" deck="1"/>


mapping:

<map value="POSDELTA" action="motorwheel3 posdelta" />
<map value="TIMEDELTA" action="motorwheel3 timedelta" />


Using just
  <map value="POSDELTA" action="jogwheel" /> 

moves the track at almost correct speed, so I assume the position value is correct but the time counter isn't.

Bedankt voor de hulp!
 

Mensajes Mon 30 Jan 23 @ 12:25 pm
AdionPRO InfinityCTOMember since 2006
Did you try to add '1000' as mentioned for the mapping?
motorwheel3 timedelta 1000

The default value when not adding '1000' is not 1000.
 

Mensajes Mon 30 Jan 23 @ 1:07 pm
Yes, but it didn't change much.

I also tried this:
report counter with just microseconds, no other scaling.
definition with jog full="65535" i.e. the full 16bit range is used
and a mapping with
motorwheel3 timedelta 65536


no luck... :/
 

Mensajes Mon 30 Jan 23 @ 2:35 pm
Could it be that motorwheel3 posdelta expects 33RPM while jogwheel requires 45RPM?

When I run the position at 33RPM, with the motorwheel3 posdelta mapping, it runs almost correctly but only as long as the track is not in play mode (still the occasional nudge/glitch though..) As soon as I press play, it runs very very slow.

With the jogwheel mapping there's no difference between being paused but having the jogwheel move the track or in play mode.
 

Mensajes Mon 30 Jan 23 @ 4:15 pm
AdionPRO InfinityCTOMember since 2006
The rpm should follow the RPM setting.
 

Mensajes Mon 30 Jan 23 @ 4:24 pm
So I got it running.. but during play I have to scale position by 16.. as if I'm running 16x33rpm. Guessing It would give higher resolution needed for accurate playback.

Is that expected? I should scale my position output depending on the play/pause state?
i.e. when jogging during pause I send 33rpm, during play I send 16x33rpm?
(or 45rpm depending on the config)
 

Mensajes Mon 30 Jan 23 @ 4:41 pm
The resulting "speed" is always governed by the following three settings:
jogSensitivityScratch (default value = 1)
jogSensitivityCue (default value 0.5)
jogSensitivityBend (default value 0.05)

These values are multipliers.
Your controller should be defined in a way that produces the correct playback speed when the deck is playing (and therefore jogSensitivityScratch multiplier is used)
The easiest way to test this is to clone the motorized controlled deck while playing to a non motorized deck and watch if it drifts ahead or behind.

If you just set your jog to rotate while the deck is not in "play" status then the jogSensitivityCue multiplier is used. Which with the default values means that the deck should play at around half the normal speed.

Obviously in your case jogSensitivityBend is not used since you are using a motorized jog. On a non motorized jog it's used when the user spins a "touchwheel" without actually touching the top area (and thus pitch bending the track instead of scratching it)
 

Mensajes Mon 30 Jan 23 @ 9:00 pm
I managed to get it running at the correct speed. It was the time delta value that was not correct.

I assumed with the definition having full=16384 and the mapping motorwheel3 timedelta 1000
That a value of 16384 should correspond to 1000 microseconds / 1ms:

uint64_t time =  esp_timer_get_time(); //microseconds
uint16_t time_vdj = (uint16_t)((time * 16384 / 1000)) ; //'full' 16384 = 1000us... this is wrong


but that's not the case. It seems it just wants microseconds, no scaling. Not sure what the 16384 value in the mapping means.. but it works now.

I do have a problem when I stop the wheel. The track doesn't stop but slowly drifts. Seemingly in the last direction the platter was moving before the stop. I looks like vdj thinks the controller is not sending updates anymore so it keeps moving at the last speed it detected? Is this expected behaviour? Can I configure that somehow?

If not, should I start/stop the track explicitly? How would I map a toggle element (1 bit, on = play...) to play/pause?

 

Mensajes Tue 14 Feb 23 @ 10:40 am
AdionPRO InfinityCTOMember since 2006
Ideally you continue to send updates but without movement. (you could reduce sending rate or stop after a while to lower processing power)

You can also combine it with sending pause/cue to vdj so that the motorwheel options in vdj will work properly, ignoring motor updates a few seconds after stop to prevent wobble
 

Mensajes Tue 14 Feb 23 @ 12:35 pm
I do keep sending updates. motorwheel3 timedelta keeps counting, just posdelta has the same value.. but when that happens vdj starts that slow drift.
 

Mensajes Tue 14 Feb 23 @ 1:59 pm
I added play/pause messages. This puts the track correctly in pause mode whenever the platter is not moving and vice-versa automatically in play when moving.

It only solves the drifting for a second while it ignores the platter wrt motorWheelLockTime, after it starts drifting again.
 

Mensajes Tue 14 Feb 23 @ 4:01 pm
I noticed the rane twelve mapping uses ns7_platter and ns7_platter 'timestamp'

Maybe I have better luck with those. What's the expected data for the timestamp?
 

Mensajes Tue 14 Feb 23 @ 4:28 pm
I managed to get it working.. but it's a bit hacky. I just assure I don't have any repeating position values.

When the platter is stopped I add some noise (position counter +5 if it would be the same value, I scaled the jog values to have a bigger range, 10x the actual encoder count so +5 is half a step out of 5760 and not noticeable, track looks completely stable)

This can't be the proper solution so I'd love to get a real fix for this. I could provide you with the code to program an esp32-s3 to replicate the problem, or even mail you one that's ready for testing.

Once I have the bugs ironed out I'll release the code with definition and mapping files to have full HID (platter) controller example code available.
 

Mensajes Tue 14 Feb 23 @ 8:42 pm
Hello Beau, have you success ?
I am trying to make a motorized controller too.
i get too much jitter .
I am using midi messages.

regards
 

Mensajes Thu 11 May 23 @ 9:12 pm
Hi, sorry for the late reply. I didn't see you post.

Yes, I got it working fine now. Still got some issues when trying to match the platter speed to the pitch setting though.

I think midi might be too slow for this though. Did you get it working?
 

Mensajes Sat 05 Aug 23 @ 11:26 am