Mapping Numark V7 motorized turntable to OtsAV

DarrylB

2016-02-20 13:56:21

Hi everyone.

I'm a total newbie to Bome MT. I've downloaded the trial to see if it can do what I need it to and my first impression is that it can but I need some help.

The Numark V7 has a motorized platter with a piece of vinyl on top. The position of the platter is spat out very frequently in the form of a CC value and also as a pitch bend value that I cannot make head nor tail of.

The OtsAV DJ software can handle a velocity value for scratching and pitch/tempo bending and I need to convert from this absolute time based incremental value to an indication of how fast the platter is rotating (velocity value - as sent by e.g. Denon HC4500 controller that Ots Labs already supports).

here is an example of the Bomes log with Deck A spinning at 33 1/3 RPM (AFAIK - it can do both 33 and 45 RPM).

Code: Select all

332626 - MIDI IN [Numark V7 MIDI]: B0 00 08
332626 - IN   1.0  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=8 (0x08)
332626 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=8 (0x08)
332626 - MIDI IN [Numark V7 MIDI]: E0 00 7F
332626 - IN   0.0  Pitch Bend on channel 1 with any bend=8064 (0x1F80)
332628 - MIDI IN [Numark V7 MIDI]: B0 00 0B
332628 - IN   1.0  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=11 (0x0B)
332628 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=11 (0x0B)
332628 - MIDI IN [Numark V7 MIDI]: E0 3F 17
332628 - IN   0.0  Pitch Bend on channel 1 with any bend=-5185 (0xFFFFEBBF)
332628 - MIDI IN [Numark V7 MIDI]: B0 00 0E
332629 - IN   1.0  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=14 (0x0E)
332629 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=14 (0x0E)
332629 - MIDI IN [Numark V7 MIDI]: E0 7E 2F
First question - is the timestamp in Bomes MT in milliseconds?

DarrylB

2016-02-20 14:14:50

BTW I'm having to work everything out for this controller myself since Numark made it in conjunction with ITCH and haven't published any of the MIDI message information (or at least, I couldn't find it when I looked).

I took the unit apart and took a high-res photo of the optical encoder wheel - it has 901 marks but since it needs to know which direction it is spinning in it uses a quadrature encoder which has 2 sensors offset 90 degrees out of phase so that one gets blocked before the other - from logging the values and carefully rotating the vinyl by hand I can see that the value increases 4 times for each mark on the wheel giving 901 * 4 = 3604 values for a complete rotation.

I've just verified that is actually running at 45 RPM (I put a 60 timer on and counted - high tech stuff!)

So please can one of you sanity check my maths:
At 3604 'ticks' per rotation at 45 RPM, I make that 162180 ticks per minute.
Dividing by 60 gives 2703 ticks per second

The changes in the very small sample I took earlier showed it going up 3 ticks per MIDI message. I imagine that since it should be 2.703 per millisecond and we are dealing with integer values, occasionally the difference is just 2 ticks.

So now I need to work out how to change that into a velocity figure - where 0 is the deck isn't spinning at all - to 127 where the deck is spinning at a sensible multiple of the normal speed - whatever rotational velocity is actually needed for scratching/backspins etc.

DarrylB

2016-02-20 14:44:11

I've just realised that my previous idea of 0 to 127 for full rotation wont work - we can only have 0 to 63 for rotating forwards and 127 to 64 for backwards. Looking at the manual for the Denon MC2000 (http://b06ba727c886717f9577-fff53f92784 ... IM_v00.pdf) this is typical - they use 0x41 (65) as slow forwards -> 0x7F (127) as fast forward and 0x39 (63) as slow back to 0x00 (0) as fast back making 0x40 (64) the value when the wheel is stopped.

So - MIDI in from this controller is 2.703 ticks per message at 45RPM. Can I assume then that the corresponding velocity value for 'normal playback speed' would be half way on the range - so if 0x40 (64) is stopped and 0x7f (127) is max speed (whatever that is) do I say that halfway (96) is 'normal' speed and give the 32 values of scratch speed (per millisecond) for slower and faster in each direction?

I logged a few more - I was assuming that the controller was sending these messages once a millisecond - it's not - they are coming out as soon as the hardware detects the pulse - but if that was the case it would go up by 1 for each rise/fall of a sensor - but I expect that is too rapid for MIDI, so they keep a track of the tick count in hardware and throttle it - hence the value sometimes going up by 3, sometimes by 2.

Here's just the CC messages at 45RPM:

Code: Select all

312793 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=45 (0x2D)
312794 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=48 (0x30)
312795 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=51 (0x33)
312796 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=53 (0x35)
312797 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=56 (0x38)
312799 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=59 (0x3B)
312800 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=62 (0x3E)
312801 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=64 (0x40)
312801 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=67 (0x43)
312802 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=70 (0x46)
312803 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=72 (0x48)
312804 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=75 (0x4B)
312805 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=78 (0x4E)
312807 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=81 (0x51)
312808 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=83 (0x53)
312810 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=86 (0x56)
312810 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=89 (0x59)
312811 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=91 (0x5B)
312811 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=94 (0x5E)
And here my son span it forward as fast as he could while I started and stopped the logging in Bomes MT:

Code: Select all

608877 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=24 (0x18)
608878 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=43 (0x2B)
608879 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=61 (0x3D)
608881 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=80 (0x50)
608881 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=99 (0x63)
608882 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=117 (0x75)
608884 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=7 (0x07)
608885 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=26 (0x1A)
608886 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=44 (0x2C)
608887 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=63 (0x3F)
608887 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=81 (0x51)
608888 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=99 (0x63)
608889 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=117 (0x75)
608890 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=7 (0x07)
608892 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=25 (0x19)
608893 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=43 (0x2B)
608894 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=61 (0x3D)
608895 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=79 (0x4F)
608895 - IN   1.1  Control Change on channel 1 with CC#:0 (0x00) set 'pp' to value=97 (0x61)
So this leads me to think that I should keep the previous value in a variable and use the new value to calculate the velocity (taking into account the wrap-around). So can I find out how long it was since the last message was received or do I need to calculate the velocity using a timer?

DarrylB

2016-02-20 14:44:59

Sorry for the amount of posting and the length, but I wanted to give as much information as possible.

DarrylB

2016-02-20 20:01:10

Readers of this thread might just want to treat it as my thinking out loud. I don't know how busy this forum gets and it's a Saturday when people often have better things to do than read some bloke's random musings on how to use a DJ controller with something it wasn't designed for!

I think I've figured out how Bome MT can do this but I'm struggling with the detail.

1. Create a translator to create and start a velocity measuring timer from from the incoming trigger 'project opened' and int he rule initialize two global variables to unset (magic number) for the current tick and previous tick (the magic number being an impossible value - minus 1 since the tick can only have values from 0 to 127). Lets call latest tick g1 and previous tick g2. This timer will be set to fire every 10ms for ever.
2. I create a MIDI translator (for each deck but for now I'll just try to get one working) that handles the CC message for the vinyl position and swallows it, storing it's 'tick' value in the current tick global variable (g1).
3. Create a translator whose input is the velocity timer event with rules set so that if the previous tick is -1, just sets previous tick to current tick - i.e. g2=g1 and stops processing. If previous tick isn't -1 and current tick isn't -1 (the vinyl may not have been moved yet) stop processing. If the previous tick and current tick values differ we know the change occurred in between the two timers, so we can calculate the velocity and put the MIDI velocity value into a local variable and have the outgoing action generate the Ots AV velval MIDI message (to match one of the known/tested Denon ones).

Any thoughts on this approach? I'd prefer that the timer didn't have to run all the time if the deck isn't even spinning, but although you can start a timer from a MIDI message (i.e. the vinyl moving) subsequent starts will reset the timer delay according to section 9.3.2 of the manual: "If you start a Timer that is already running, the delay time will be refreshed with the new delay time. "

So I don't know if that means that say the next message comes in 1ms after I've started my 10ms timer, does the timer fire after the first 10ms with the delay time reset to the same value - or does it mean that timer event won't fire until the 10ms have elapsed from now? Given that this controller will output these MIDI messages more frequently, I don't know if the timer event will ever trigger.

I think I'll just have to try it - I'm quibbling over the slight ambiguity in the description in the manual - i.e. is the current timer event affected by the new start or does the new start just change the delay, so in this case (setting it to 10ms again) would have no affect.

If the restart of the same timer doesn't affect the timer that is currently running, then when I detect the vinyl has stopped in step 3 I could presumably kill the timer.

DarrylB

2016-02-21 23:43:53

In case anyone cares to know - further experimentation and reviewing some documentation that Ots Labs wrote for their velocity based control MIDI handler - it is expecting to be told the delta of pulses - so I don't need the timer at all, I've just got to use Bomes to work out the delta since the last message and handle the wrap around.

I've got it working for forward platter rotation - now I just need to tweak the smoothing values in the OtsAV OHML file.

DvlsAdvct

2016-02-22 22:41:54

Hey DarrylB

Sorry for not getting back to you. I try to keep up with the forum on my off-time, but it has been a hectic few weeks. I'm the support guy. Florian, who lurks around when he has time, is the architect of MT Pro.

Your work looks very interesting. I'll keep my eye on the thread if more problems come through. :)

DarrylB

2016-02-23 10:03:48

The only thing I'm struggling with is how to change an absolute value that goes from 0-127 into a delta.

I read a post from Florian on how to do this for a knob control but it wasn't clear at the end of the thread whether it fully worked or whether it fully applies to this application.

Florian's suggestion was to use one translator to handle positive deltas and one to handle negative. So I started on the positive delta translator and got good forward result, however rotating the platter backwards resulted in some MIDI codes going out with values greater than 127 (0x7f).

I'm going to keep the previous known value of the position for deck A in ga and the previous value for deck B in gb. To avoid an invalid initial calculation I will initialise the value of ga and gb to -1 (since valid values are 0 to 127) using a translator driven by project load.

So on receipt of the MIDI event from the controller, I receive the new absolute position in pp. I can calculate the delta with qq=pp-qa resulting in a typical positive value for forward spin and negative value for backward spin (as long as the counter has not wrapped around).

But there are cases where I'll get a negative value for forward wrap-around and large positive values for backward wrap-around that I need to handle.

The issue is that the calculated delta needs to be in a certain range and there is no logical and on conditional expressions - to do something like if qq < 0 && qq > -63 then exit processing, skip outgoing action.

Since I can't do that in MT I'll have to handle these in separate if statements. I've just got to work out the logic first then I can inverse the && and find a condition to exit on such as if qq > -63 then exit - but do these checks in the correct order.

I just need to find the correct logic and a means of testing it without having to sit there spinning the controller.
I'll give it another try tonight

florian

2016-02-25 01:31:44

Hi DarrylB,
thanks for posting your "evolution" here on the forum.

Getting the math right is often quite hard! Sometimes it helps a lot to pen down what you really want to do, in English, or algebra, or what you're comfortable with. Then convert it slowly to become Translators and Rules in MT Pro...

For example:

Code: Select all

if qq < 0 && qq > -63 then exit processing, skip outgoing action
A cheap (but working) way is to use Goto:

Code: Select all

if qq >= 0 then GOTO "OK"
if qq > -63 then exit processing, skip outgoing action
Label "OK"
Note that labels must be unique in a given Rules section.
Also, it might be safe to assume that qq never goes below -63? Just a guess though...

In any case, if you post a set of rules, explaining what they SHOULD do, but what they ACTUALLY do, it might be easy for DvlsAdvct (or me) to fix it.

Thanks,
Florian