Quick Sign In:  

Forum: VirtualDJ Plugins

Topic: Timecode plugin development - Page: 1

This part of topic is old and might contain outdated or incorrect information

KhyewHome userMember since 2008
I have a few questions about the functions and variables prototyped in vdjTimecode.h that the inline comments could not answer.

1. In the IVdjPluginTimecodeSignal class, the Decode method is called with a pointer to "a signed 32bits interleaved stereo buffer of nb stereo incoming samples". Is nb a predictable value? Will Decode be called at regular intervals every time nb samples have populated the buffer, or is it called at every opportunity with a value of nb reflecting the number of samples stored since the last call?

2. In IVdjPluginTimecodeSignal:
int LastSyncCode; // value of the last timecode read-out (in 1/44100s)

Why is this not an unsigned integer? Currently it can store only a maximum offset value of approx +13 minutes. Surely negative values are not required when reporting a time offset, and there do exist time-coded vinyls with up to 17 minutes of time codes.

3. In IVdjPluginTimecodeSignal:
int UnsyncedLength; // time elapsed since the last valid timecode was found
How is time calculated? Is it a simple counter that is incremented every time Decode is called and does not produce a valid time code?

I'm looking forward to expanding the feature set of VirtualDJ. Answers to these questions would help greatly.
 

Posted Fri 11 Jan 08 @ 3:21 pm
Paz75PRO InfinityMember since 2006
give the man a free license and then chain him to the workstation and whip him till he cranks out a few hundred cool plugins... ;-)
 

Posted Sun 13 Jan 08 @ 9:02 am
KhyewHome userMember since 2008
I'm doing for a friend who demands that his Serato time-coded vinyls work in VirtualDJ before purchasing a Pro license. I don't DJ myself.

Is there any place where I can get these questions answered? Also, is there a way for plugins to output text debug information?
 

Posted Sun 13 Jan 08 @ 9:45 am
djcelPRO InfinityModeratorMember since 2004
As soon as Decode() returns S_OK, VirtualDJ will refill the sound buffer with some new nb samples

nb is defined by the latency in the VirtualDJ config
 

Posted Sun 13 Jan 08 @ 10:09 am
SBDJPRO Infinity Member since 2006
Khyew wrote :
Also, is there a way for plugins to output text debug information?


Yes; you can sprintf to the plugin window if you declare it, or even easier just run the project in Debug from VS and use OutputDebugString to print to the debug output pane.

Regards,

Scott
 

Posted Sun 13 Jan 08 @ 10:57 am
Dev staffHome userSenior staffMember since 2003
Hi.

1) As DJCel replied, nb will be equal to the latency set by the user. So it's mostly constant, but it can change without notice if the user goes in config pannel and change the latency setting.
So you can store the value of nb the first time your Decode() gets called, and use it as constant (to initialize arrays etc), but be sure at the begining of Decode to check if nb still match your stored constant value.

2) Check your maths again. MAXINT=0x7FFFFFFF=50 minutes of samples.

3) LastSyncCode and UnsyncedLength are values that should be filled in by TimecodeSignal plugins. They are used by TimecodeEngine plugins.
When you write TimecodeSignal plugins, fill LastSyncCode with the latest valid timecode you could read, and UnsyncedLength with the number of samples you read but couldn't match to a valid timecode since that.
 

Posted Mon 14 Jan 08 @ 9:35 am
KhyewHome userMember since 2008
Thanks. Yeah, my math in 2) got borked bad. Don't know what I was smoking.
 

Posted Mon 14 Jan 08 @ 1:57 pm
KhyewHome userMember since 2008
Hi all,

I recently got around to starting this project, but it appears that I've hit a wall already. My plugin does not seem to load. VirtualDJ does not display any of the plugin's information when it is selected in the plugin browser (effects->external devices->timecode signal). In addition, using the auto-config button in the timecode config dialog does not set the input gain to zero (a behavior that I coded into the plugin as a test). The plugin does appear as a selectable option in both the timecode config dialog under the "signal" drop-down menu, as well as in the plugin browser.

I haven't found any examples online for programming a timecode signal plug-in; I tried to look at the effects and mapper examples and apply them appropriately to my plug-in to the best of my abilities. Therefore I hope my mistake is small and results from my ignorance. I appeal to the development community for assistance.

Developers, lend me your eyeballs:

#include <windows.h>
#include <stdlib.h>
#include "vdjPlugin.h"
#include "vdjTimecode.h"

class CSerato : public IVdjPluginTimecodeSignal
{
public:
HRESULT __stdcall OnGetPluginInfo(TVdjPluginInfo *infos);

// called when timecode is activated on that deck
HRESULT __stdcall Init();

HRESULT __stdcall Decode(int *buffer,int nb);

// AutoConfig is called if the user click on the auto button on the config page
// The buffer is one second of signed 32bit interleaved stereo buffers (nb=44100)
HRESULT __stdcall AutoConfig(int *buffer,int nb);

};

/* export our timecode plugin's class to VDJ */
HRESULT __stdcall DllGetClassObject(const GUID &rclsid,const GUID &riid,void** ppObject)
{
if(memcmp(&rclsid,&CLSID_VdjPlugin,sizeof(GUID)) != 0) return CLASS_E_CLASSNOTAVAILABLE;
if(memcmp(&riid,&IID_IVdjPluginTimecodeSignal,sizeof(GUID))!=0) return CLASS_E_CLASSNOTAVAILABLE;

*ppObject= new CSerato();
return NO_ERROR;
}

/* CSerato member functions */

HRESULT __stdcall CSerato::OnGetPluginInfo(TVdjPluginInfo *infos) {
infos->PluginName = "Serato timecode signal processor";
infos->Author = "QED";
infos->Description = "Enables the use of Serato time-coded vinyls";
infos->Flag=0;
return S_OK;
}


HRESULT __stdcall CSerato::Decode(int *buffer, int nb) {
return S_OK;
}

HRESULT __stdcall CSerato::Init() {
return S_OK;
}

HRESULT __stdcall CSerato::AutoConfig(int *buffer, int nb) {
Config->ClearSound = 0;
Config->Gain =0;
return S_OK;
}
 

Posted Sun 03 Feb 08 @ 6:01 pm
djcelPRO InfinityModeratorMember since 2004
It's strange because it works for me. Here is my base.

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include "vdjTimecode.h"

//////////////////////////////////////////////////////////////////////////
// Class definition
//////////////////////////////////////////////////////////////////////////
class CTC : public IVdjPluginTimecodeSignal
{
public:
// Plugin
HRESULT __stdcall OnLoad();
HRESULT __stdcall OnGetPluginInfo(TVdjPluginInfo *infos);
ULONG __stdcall Release();
HRESULT __stdcall OnParameter(int id);
// Timecode
HRESULT __stdcall Init();
HRESULT __stdcall Decode(int *buffer,int nb);
HRESULT __stdcall GetQuality(int *quality);
HRESULT __stdcall GetCustomDisplay(DWORD *Bits,int Width,int Height);
HRESULT __stdcall AutoConfig(int *buffer,int nb);

private:

};
//////////////////////////////////////////////////////////////////////////
// Initialization
//////////////////////////////////////////////////////////////////////////
HRESULT __stdcall DllGetClassObject(const GUID &rclsid,const GUID &riid,void** ppObject)
{
// This is the standard DLL loader for COM object.
// You don't need to change anything in this function.
if(memcmp(&rclsid,&CLSID_VdjPlugin,sizeof(GUID))!=0) return CLASS_E_CLASSNOTAVAILABLE;
if(memcmp(&riid,&IID_IVdjPluginTimecodeSignal,sizeof(GUID))!=0) return CLASS_E_CLASSNOTAVAILABLE;
*ppObject=new CTC();
return NO_ERROR;
}
//------------------------------------------------------------------------------------------
HRESULT __stdcall CTC::OnLoad()
{

OnParameter(0);
return S_OK;
}
//------------------------------------------------------------------------------------------
HRESULT __stdcall CTC::OnGetPluginInfo(TVdjPluginInfo *infos)
{
infos->Author="DJ CEL";
infos->PluginName="TCV Signal";
infos->Description="Beta 1";
infos->Flag=0;
return S_OK;
}
//------------------------------------------------------------------------------------------
ULONG __stdcall CTC::Release()
{
delete this;
return 0;
}
//------------------------------------------------------------------------------------------
HRESULT __stdcall CTC::OnParameter(int id)
{

return S_OK;
}
//////////////////////////////////////////////////////////////////////////
// Timecode behavior
//////////////////////////////////////////////////////////////////////////
HRESULT __stdcall CTC::Init()
{

return S_OK;
}
//------------------------------------------------------------------------------------------
HRESULT __stdcall CTC::Decode(int *buffer,int nb)
{

return S_OK;
}
//------------------------------------------------------------------------------------------
HRESULT __stdcall CTC::GetQuality(int *quality)
{

return S_OK;
}
//------------------------------------------------------------------------------------------
HRESULT __stdcall CTC::AutoConfig(int *buffer,int nb)
{


return S_OK;
}
//------------------------------------------------------------------------------------------
HRESULT __stdcall CTC::GetCustomDisplay(DWORD *Bits,int Width,int Height)
{


return S_OK;
}


 

Posted Mon 04 Feb 08 @ 7:58 am
SBDJPRO Infinity Member since 2006
Tested your code OK here too, with one exception:

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <stdlib.h>
#include "vdjPlugin.h"
#include "vdjTimecode.h"

class CSerato : public IVdjPluginTimecodeSignal
{
public:
HRESULT __stdcall OnGetPluginInfo(TVdjPluginInfo *infos);

// called when timecode is activated on that deck
HRESULT __stdcall Init();

HRESULT __stdcall Decode(int *buffer,int nb);

// AutoConfig is called if the user click on the auto button on the config page
// The buffer is one second of signed 32bit interleaved stereo buffers (nb=44100)
HRESULT __stdcall AutoConfig(int *buffer,int nb);

};

/* export our timecode plugin's class to VDJ */
HRESULT __stdcall DllGetClassObject(const GUID &rclsid,const GUID &riid,void** ppObject)
{
if(memcmp(&rclsid,&CLSID_VdjPlugin,sizeof(GUID)) != 0) return CLASS_E_CLASSNOTAVAILABLE;
if(memcmp(&riid,&IID_IVdjPluginTimecodeSignal,sizeof(GUID))!=0) return CLASS_E_CLASSNOTAVAILABLE;

*ppObject= new CSerato();
return NO_ERROR;
}

/* CSerato member functions */

HRESULT __stdcall CSerato::OnGetPluginInfo(TVdjPluginInfo *infos) {
infos->PluginName = "Serato timecode signal processor";
infos->Author = "QED";
infos->Description = "Enables the use of Serato time-coded vinyls";
infos->Flag=0;
return S_OK;
}


HRESULT __stdcall CSerato::Decode(int *buffer, int nb) {
return S_OK;
}

HRESULT __stdcall CSerato::Init() {
return S_OK;
}

HRESULT __stdcall CSerato::AutoConfig(int *buffer, int nb) {
Config->ClearSound = 0;
Config->Gain =0;
return S_OK;
}


Without that definition the code wouldn't compile for me at all. Once added, your code compiled and the plugin appeared in the browser. Clicking on it showed the plugin information.

You can also implement the OnLoad() function - this will be called as soon as your plugin is loaded - for example when it's selected in the browser - which can be very handy for troubleshooting.

I added this to your plugin, and sure enough the OnLoad and Init events were fired fine :) I couldn't test the AutoConfig though, as I don't have an ASIO device here at work.

Regards,

Scott
 

Posted Mon 04 Feb 08 @ 10:19 am
SBDJPRO Infinity Member since 2006
OK, installed ASIO4ALL to test completely for you. I set my project to use the Multi-Byte character set rather than unicode (so OutputDebugString would work easily) then compiled this:

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <stdlib.h>
#include "vdjPlugin.h"
#include "vdjTimecode.h"

class CSerato : public IVdjPluginTimecodeSignal
{
public:
HRESULT __stdcall OnGetPluginInfo(TVdjPluginInfo *infos);

// called when timecode is activated on that deck
HRESULT __stdcall Init();
HRESULT __stdcall OnLoad();

HRESULT __stdcall Decode(int *buffer,int nb);

// AutoConfig is called if the user click on the auto button on the config page
// The buffer is one second of signed 32bit interleaved stereo buffers (nb=44100)
HRESULT __stdcall AutoConfig(int *buffer,int nb);

};

/* export our timecode plugin's class to VDJ */
HRESULT __stdcall DllGetClassObject(const GUID &rclsid,const GUID &riid,void** ppObject)
{
if(memcmp(&rclsid,&CLSID_VdjPlugin,sizeof(GUID)) != 0) return CLASS_E_CLASSNOTAVAILABLE;
if(memcmp(&riid,&IID_IVdjPluginTimecodeSignal,sizeof(GUID))!=0) return CLASS_E_CLASSNOTAVAILABLE;

*ppObject= new CSerato();
return NO_ERROR;
}

/* CSerato member functions */

HRESULT __stdcall CSerato::OnGetPluginInfo(TVdjPluginInfo *infos) {
infos->PluginName = "Serato timecode signal processor";
infos->Author = "QED";
infos->Description = "Enables the use of Serato time-coded vinyls";
infos->Flag=0;
return S_OK;
}


HRESULT __stdcall CSerato::Decode(int *buffer, int nb) {
return S_OK;
}

HRESULT __stdcall CSerato::Init() {
OutputDebugString("Initrn");
return S_OK;
}

HRESULT __stdcall CSerato::OnLoad() {
OutputDebugString("OnLoadrn");
return S_OK;
}

HRESULT __stdcall CSerato::AutoConfig(int *buffer, int nb) {
Config->ClearSound = 0;
Config->Gain =0;
OutputDebugString("AutoConfigrn");
return S_OK;
}


On running, in the debug output window sure enough I got "OnLoad", then "Init". Once I went into the VDJ timecode config and clicked autoconfig I got "AutoConfig" and the parameters set themselves accordingly.

Also, you don't need to implicitly include vdjPlugin.h as vdjTimecode.h will include it's requirements for you.

I hope this helps you somehow :)

Regards,

Scott
 

Posted Mon 04 Feb 08 @ 10:35 am
KhyewHome userMember since 2008
@Scott: I did not have to define the WIN32_LEAN_AND_MEAN macro because version 1.1 of vdjPlugin.h defines it. I found the file in this forum category, under the stickied thread titled "Topic: Plugins SDK for VirtualDJ v5.x and Numark CUE (PC & MAC)"

@Everyone else:
It's strange that the plugin works without any further modification for you guys. I am using Dev-Cpp to program this plugin, which I believe uses the GNU 3.xx toolchain for compilation. I may head down and pick up my free copy of Microsoft Visual Studio 2005 from the university's CS building; I doubt that a change in compiler will make a difference, but the ability to use DebugOutputString will be useful.

Are you guys all using the version of vdjPlugin.h and vdjTimecode.h available from http://www.virtualdj.com/developers/?

--
Moderated by Lady Cameron
Sorry Khyew no email address is allowed from a demo user
 

Posted Mon 04 Feb 08 @ 2:30 pm
djcelPRO InfinityModeratorMember since 2004
Khyew wrote :
Are you guys all using the version of vdjPlugin.h and vdjTimecode.h available from http://www.virtualdj.com/developers

I use both: the default one and the modified one. Both work
 

Posted Mon 04 Feb 08 @ 3:32 pm
djcelPRO InfinityModeratorMember since 2004
Ok I think I have just understood your problem. If you use the modified .h, do not write in your .cpp #include < windows.h> again because it's already included in .h
 

Posted Mon 04 Feb 08 @ 3:52 pm
KhyewHome userMember since 2008
@djcel: There is no problem with the pre-processing or compilation. Including windows.h in the plugin source file neither resolves nor introduces any problem because there is a directive in vdjPlugin.h that includes it, complete with include guard to protect against multiple inclusions.

If I had a problem with pre-processor directives and macros, gcc would have barfed instead of produce a non-working DLL. Thanks for trying though.


@Everyone else: I compiled the project under Visual Studio 2005 with no changes and it works fine. I have no idea why gcc does not produce a working plugin out of my source, but I hope it's not a fundamental issue with VirtualDJ. Other plugin writers may be greatly inconvenienced by this behavior. If any hackers want to determine the cause of this problem they can feel free to try and reproduce it. Dev-C++ version 4.9.9.2, compilation and linkage performed by the version of the mingw toolchain that comes with the full installation of Dev-C++. Enjoy.

Thanks for all your help guys.

--
Moderated by Lady Cameron
Sorry Khyew no email address is allowed from a demo user
 

Posted Mon 04 Feb 08 @ 5:58 pm
djcelPRO InfinityModeratorMember since 2004
I have well received your dll

PS: For security reasons, I have hidden my e-mail ;-)
 

Posted Mon 04 Feb 08 @ 6:32 pm
djcelPRO InfinityModeratorMember since 2004
The dll that you have just sent me can be seen in the plugin browser but it crashes VirtualDJ when we load it in the timecode config.

Are you sure to have MSVCRT.dll on your computer because your plugin asks it

PS: Visual C++ 2005 will ask MSVCRT80.dll
 

Posted Mon 04 Feb 08 @ 6:36 pm
KhyewHome userMember since 2008
The plugin doesn't crash VirtualDJ on my machine when I load it in the timecode config but that may be due to slightly different operating environments. I'm running Windows XP with Service Pack 2 installed. It simply fails to work despite being available in the plugin browser and the timecode configuration window.

I'll check if I have MSVCRT.dll. Curse these dynamic linkages!
 

Posted Mon 04 Feb 08 @ 6:47 pm
djcelPRO InfinityModeratorMember since 2004
It's strange because in your dll, you have 2 export functions (whereas you only need one for VirtualDJ):
DllGetClassObject
and
DllGetClassObject@12

And MSVCRT.dll is loaded twice
 

Posted Mon 04 Feb 08 @ 6:48 pm
KhyewHome userMember since 2008
It is my understanding that the following code for the Decode method should cause the active deck to play at 2x the normal speed of a song:

HRESULT __stdcall CSerato::Decode(int *buffer, int nb) {
// let's make this sucker only go forward at 1x speed for now
LastSyncCode += 2*nb;
Direction=1;
DeltaPosSinceSync = 2*nb;
UnsyncedLength=0;
SilenceLength=0;

sprintf(msgbuf, "DEBUG: LastSyncCode %i, nb size %i\n", LastSyncCode, nb);
OutputDebugString(msgbuf);
return S_OK;
}

Actual result when timecode is activated on the deck: nothing. On relative and smart mode the deck does not move, and on absolute mode the deck jumps to the beginning of the song and stay there. Is my initial hypothesis wrong? Do I have some misconceptions in my design? Assuming the plugin does not bork anywhere else (the debug window gives me a steady stream of increasing LastSyncCode output), is there something else I'm supposed to do to get VirtualDJ engine to register the change in sync code?

Postscript: I'm using the default VirtualDJ engine with my signal plugin. That works, yes?
 

Posted Thu 06 Mar 08 @ 9:55 pm
49%