Sonique Vis

Started by Irrational86,

Irrational86

Umm...Ian, can you please help me a bit on how do sonique vis work? I dont mean filling in the info and all that, i know about that, but the Video thing, how does it work? I was reading some threads from before, and you mention a GDI Bitmap, whats that? About DirectDraw, i might be able to go that way, but whats a GDI Bitmap? Please help  ::)

Ian @ un4seen

Yep, you can use either a GDI bitmap, or a DirectDraw surface, and the bitmap/surface must be 32 bits-per-pixel. There's an example of using a GDI bitmap in the SPECTRUM and LIVESPEC examples.

Irrational86

Ian, i was trying to translate the C source of the GDI bitmap to delphi, but i dont get something...

you declare an array of 2000 bytes, from the first byte to size of BITMAPINFOHEADER is the bh pointer and the pal RGBQuad starts where the bh structure ends..

now, what do you use the pal for? its declared local, and the functions below dont use it, can you please explain this a bit?

Ian @ un4seen

When creating a device independant bitmap (DIB), the palette comes at the end of the header. So "pal" is a pointer to the end of the header, and the header gets passed to CreateDIBSection.

But 32 bpp bitmaps don't have a palette, so it's actually even simpler than in those examples :)
HDC visdc;
HBITMAP visbmp;
BYTE *visbuf;

...

BITMAPINFO bi={0};
bi.bmiHeader.biSize=40;
bi.bmiHeader.biWidth=VISWIDTH;
bi.bmiHeader.biHeight=-VISHEIGHT;
bi.bmiHeader.biPlanes=1;
bi.bmiHeader.biBitCount=32;
visbmp=CreateDIBSection(0,&bi,DIB_RGB_COLORS,&visbuf,0,0);
visdc=CreateCompatibleDC(0);
SelectObject(visdc,visbmp);

...

visinfo->Render(visbuf,VISWIDTH,VISHEIGHT,VISWIDTH,&visdata);
BitBlt(targetdc,targetx,targety,VISWIDTH,VISHEIGHT,visdc,0,0,SRCCOPY);

Irrational86

#4
Ian, thanks a lot man  ;D ...everything worked awesome, now one thing i cant get, i dont think am coping the info right to the visdata structure, when i copy what bass returns, it just display the max height of the spectrum...like if the sound was higher then the max..i cant really explain it, its something like that..

Ian @ un4seen

Are you copying the data BASS gives to the "VisData" structure as it is? The "Waveform" data is 8-bit integers, and the "Spectrum" data is 8-bit unsigned integers (cardinals). So you need to convert the values to those first.

Irrational86

#6
unsigned 8-bit is not cardinal, cardinal is 32-bit unsigned, 8 bit unsigned goes from 0 to 255, so i guess both, the wave data and the FFT data have to be converted, right?

Irrational86

Umm..thanks a lot Ian, i got it, i kept messing around with it after i sent the last post, and i got it to work..thanks a lot man :-)

Irrational86

Ian, one question about these vis..there is one vis that when i call the render, it gives me an access violation, dont know why, is this the same thing you use for XMPlay?

HDC visdc;
HBITMAP visbmp;
BYTE *visbuf;

...

BITMAPINFO bi={0};
bi.bmiHeader.biSize=40;
bi.bmiHeader.biWidth=VISWIDTH;
bi.bmiHeader.biHeight=-VISHEIGHT;
bi.bmiHeader.biPlanes=1;
bi.bmiHeader.biBitCount=32;
visbmp=CreateDIBSection(0,&bi,DIB_RGB_COLORS,&visbuf,0,0);
visdc=CreateCompatibleDC(0);
SelectObject(visdc,visbmp);

...

visinfo->Render(visbuf,VISWIDTH,VISHEIGHT,VISWIDTH,&visdata);
BitBlt(targetdc,targetx,targety,VISWIDTH,VISHEIGHT,visdc,0,0,SRCCOPY);

Ian @ un4seen

Yup... the troublesome vis works fine in XMPlay?

Irrational86

yeah, it does, thats why i asked you if you used the same thing for the video

Jcl

Make sure you have a proper query interface. Also, some plugins make use of the FileMapping Sonique structure (which is in the updated vis sdk which you can find at sonique's webpage).

As for initialization, make sure you change the current directory to plugin's dir before attempting to load it. For some plugins i've found out that you have to do so before rendering.

Other plugins are just badly written and complain a lot about width/height/pitch. If you are using an offscreen buffer, make sure you make pitch equal to width (the pitch parameter is used when rendering directly to VGA, or having a weird buffer, where the width of the image does not match the "width" of the buffer).

In my experience, I've seen that the best thing to do with vis plugins, is a try/catch (try/except in delphi) block, because most plugins are badly coded and often try to write outside the buffer (although the rendering does succeed), so handle the access violation exception by yourself and don't worry about it :)





Irrational86

#12
Jcl or Ian, how does that QueryInterface thing work? I kinda tried, but i dont get it..little help, maybe an example?

Irrational86

Ian, can you help me with that QueryInterface thing in Sonique vis, please? Just explain or give me an example on how it works..?

Ian @ un4seen

I don't use the "QueryInterface" stuff - that's just for the plugin to be able to get info like current track name, etc... I've not seen a plugin that depended on it.

The problem is most likely what Jcl said. If the plugin uses other files (eg. a bitmap), make sure all it's files are present and you're in the same directory (SetCurrentDirectory). If that's not it, the plugin may not like the dimensions of your render buffer - try changing the width and height (particularly the width).

Jcl


QuoteI don't use the "QueryInterface" stuff - that's just for the plugin to be able to get info like current track name, etc... I've not seen a plugin that depended on it.

I did find some, but can't remember which. For sure "Cosmic Wave" (by Vovoid Multimedia IIRC) relies on the FileMapping SoniqueInfoReader thinggie for getting it's colors (and will more likely crash if it's not present, I didn't discover what was happening till i downloaded the updated vis sdk), and there were others reliyng on QueryInterface... I'm not sure if they'd even use it, but I'm sure if I didn't call "ReceiveQueryInterface" they wouldn't work. Anyway, my own plugins do use it [I create and return objects with it] :) so implement it! it's an order! hehe
(then again, not that I've released any public plugin :) )

Jcl

oh, forgot about saying how QueryInterface worked. This is how I have it implemented:

Define a class:
class QueryInterface {
public:
  virtual bool  QueryInt(char* expression, int* value);
  virtual char *QueryString(char* expression);
  virtual void  FreeString(char* String);
};

Then its implementation (a tad long, i'll copy/paste my entire implementation... most things should not be like this, and sonique plugins might use only a few of this parameters... but I don't feel like cutting out, and I don't remember which one sonique used):

// Query Interface Implementation
bool QueryInterface::QueryInt(char *expression, int *value) {
  if(!stricmp(expression, "currentsonglength")) {
    *value=(int)TGlobalInfo::time;
  } else if(!stricmp(expression, "currentsongposition")) {
    *value=(int)TGlobalInfo::time;
  } else if(!stricmp(expression, "scheme_visual_left")) {
    *value=1;
  } else if(!stricmp(expression, "scheme_visual_right")) {
    *value=1;
  } else if(!stricmp(expression, "scheme_visual_blend")) {
    *value=1;
  } else if(!stricmp(expression, "diffTime")) {
    *value=TGlobalInfo::diffTime;
  } else if(!stricmp(expression, "delete3DScene")) {
    try {
      if(value)
        delete (IMM_3DScene *)value;
    } catch(...) {
      return false;
    }
  }
  return true;
}

char *QueryInterface::QueryString(char *expression) {
  char *pp="Plugin Query Interface";
  if(!stricmp(expression, "currentSkinName")) {
    return pp;
  } else if(!stricmp(expression, "currentSongFilename")) {
    return pp;
  } else if(!stricmp(expression, "currentSongTitle")) {
    return pp;
  } else if(!stricmp(expression, "currentSongAuthor")) {
    return pp;
  } else if(!stricmp(expression, "currentSongDisplayString")) {
    return pp;
  } else if(!stricmp(expression, "stateEnable")) {
    return (char *)&StateOGL::enable;
  } else if(!stricmp(expression, "stateDisable")) {
    return (char *)&StateOGL::disable;
  } else if(!stricmp(expression, "stateGet")) {
    return (char *)&StateOGL::get;
  } else if(!stricmp(expression, "stateInit")) {
    return (char *)&StateOGL::initStatus;
  } else if(!stricmp(expression, "stateBind")) {
    return (char *)&StateOGL::bindTexture;
  } else if(!stricmp(expression, "newImage")) {
    return (char *)&newImage;
  } else if(!stricmp(expression, "allocEmptyImage")) {
    return (char *)&allocEmptyImage;
  } else if(!stricmp(expression, "imageUseBuffer")) {
    return (char *)&imageUseBuffer;
  } else if(!stricmp(expression, "freeImage")) {
    return (char *)&freeImage;
  } else if(!stricmp(expression, "loadImage")) {
    return (char *)&loadImage;
  } else if(!stricmp(expression, "currentHDC")) {
    return (char *)TGlobalInfo::hDC;
  } else if(!stricmp(expression, "currentHRC")) {
    return (char *)TGlobalInfo::hRC;
  } else if(!stricmp(expression, "textureServer")) {
    return (char *)TGlobalInfo::sharedTextures;
  } else if(!stricmp(expression, "dataPackage")) {
    return (char *)TGlobalInfo::dataPackage;
  } else if(!stricmp(expression, "newImmerseScene")) {
    return (char *)((IMM_3DScene *)new IMM_3DScene);
  }
  return NULL;
}

void QueryInterface::FreeString(char *string) {
  if(string)
     delete string;
  return;    
}

Then, instantiate an object of this class, and pass it to plugins, like this:

   QueryInterface *pluginQueryInterface;
 
   pluginQueryInterface = new QueryInterface;
    if(!pluginQueryInterface) throw(0);
 /**** blablabla ****/

    sprintf(visIni, "%s/vis.ini", appDir);
    
    if(loadPlugin(pluginFile)) {
      queryModuleProc=(QUERYMODULE)getProc("QueryModule");
      if(!queryModuleProc) throw(1);
      visInterface = queryModuleProc();
      if(!visInterface) throw(2);
      visInterface->OpenSettings(visIni);
      visInterface->Initialize();
      if(visInterface->Version>0)
        visInterface->ReceiveQueryInterface(queryInterface);
 /**** blablabla ****/

(of course, adapt to your own code... LoadPlugin only does a "LoadLibrary" and initializes some stuff, same with getProc)

have fun :)

SimWolf

For those of you who have managed to get Sonique visualization plug-ins working... I would appreciate a little bit of help.  I have them working in my program, but there are some quirks that I've not figured a way around yet.

In particular, two plug-ins (G-Force and Whitecap) don't seem to initialize correctly.  It seems that they don't find the files needed in the "[vis-name] Files" sub-folder, so I get either the default animation (Whitecap), or an error message (G-Force).  The Prefs files don't appear either.  With the same setup, XMPlay works fine... so I'm not sure what I'm doing wrong.

When I call "OpenSettings()", then "Initialize()", I have used "SetCurrentDirectory()" to be in the same folder as the SVP file beforehand.  I've even tried making that the current directory before every call to "Render()", too... but it doesn't seem to help.  I'm not sure what I'm missing.

So... if someone could explain the correct sequence to move around directories while calling OpenSettings, Initialize, etc., so as to make them behave correctly, I'd really appreciate it!  (Also... do you call OpenSettings before or after Initialize?).

Thanks for your time,
SimWolf.