What is rappa? -------------- Rappa isn't a plugin in the usual sense, in that it doesn't actually create much in the way of effects, but rather it switches between the plugins you've loaded on your system. It was originally writen just to save me from 1) having to decide which plugin I wanted to look at and 2) to save me from having to press the up/down buttons because that can be such a hardship. But mighty oaks from acorns grow, as they say, and while this is no oak, it is certainly a little more than its original brief. Rather than just switch between the two plugins I thought a nice fade effect between them might be good. Then I thought I could slide it in from the right. Then this, then that. Suddenly the transition effects have almost become a vis in their own right. Rather than be the only one who is allowed to have the fun of thinking up a writing a transition effect, I thought I would give this particular joy to the world. Some of the hardest parts of writing a vis are making it sensibly responsive to the music, and making it hold your interest for considerable time. Writing transition effects gets around both these problems as it doesn't have to be responsive to the music and it only has to be a couple of seconds long. The final step in the evolution was to make the interface a little more generic. A transition effect is essentially a way of getting from one plugin to another while doing something interesting. But it is the something interesting that is important. You can do that with just a single vis or you might want to do it with more than a start and finish vis. Hence, the rappa API. The rappa API? -------------- Before we go too far let me say that the API is expecting you to be using Visual C++ to build your plugins. You can, with a little work, probably use other C or C++ products, but I would not expect you to be able to write them in some other language, such as Visual Basic or Delphi. I'm sorry if that stops you, but that is a limitation that is too hard to get around. So what do you need to write? All you really need to write is a function that, given some number of input frames produces an output frame. You also need a standard function that rappa can look for to find the information about this plugin. We'll start with this. You need to create a function called QueryRappaPlugin which will return a pointer to a RappaPluginInfo structure (as defined in rappapi.h) that contains the information about the transition/filter effect. If you are writing in C++, this function would probably look something like extern "C" DLLEXPORT RappaPluginInfo * QueryRappaPlugin(void) { return &rpi; } Also, in order to know about these data types you need to include the rappapi.h include file. It also has information that is in the sonique vis api (vis.h - as available from the sonique site) which will also need to be included. This in term requires the windows.h include file for some types, so the top of the file needs. #include #include "vis.h" #include "rappapi.h" We also need to define the 'rpi' that we returned in the QueryRappaPlugin function. This is of type RappaPluginInfo which has 5 fields. They are Version This is the version of the API. Currently versions 0 and 1 are supported. Version 0 does not include the display and name information. nsteps The number of steps in the transition effect or filter. The number of steps is effectively the number of frames used to complete the transition or the number of frames to display the filter. Sonique vis frame rates are anything from around 5 to 40 frames per second, so a transition that took 40 steps would take anywhere from 1 to 8 seconds to run. Most of the internal transitions take from between 32 to 128 frames. The internal filters stay for around 250 steps. Your plugin function will be called once for each step number from 0 to nsteps - 1 so you can create an image for each of these frames. ninputs This is the number of vis plugins you require as input. Normally this would be 2 for a transition effect, being the original vis and the vis you are moving to. For a filter this would usually be 1, being the one vis you are filtering. Any number up to MAX_RAPPA_INPUTS (currently 8) can be used, but bear in mind that the more vises you use the slower the system will run. endinput This is which of the input vises to use after the transition or filter has completed. For example, a transition, when it completes, would stop rendering the first input and continue rendering the second, whereas a filter will end on the same vis as it started. This number is 0 based, so a filter would have a value of 0 and a transition a value of 1. The valid values are in the range of 0 to ninputs - 1. Effect This is a pointer to the function to call that actually does the render for the transition effect. I'll descibe that more later. display This is the name of the transition or effect to display. This is shown in vis mode as part of the name of the plugin running. name This is the name that should be used internally. Currently this is not used for anything, but it should consist of normal alpha (and numeric) characters and no spaces or unusual characters if possible. So if we wanted to create a simple transition effect, we would define the 'rpi' structure as follows RappaPluginInfo rpi = { 1, // Version - currently 1 64, // Some number of steps 2, // Number of inputs 1, // End input, the second one sample_Effect, "Sample", // The display name "Sample" // The internal name }; Finally, we need to write the actual effect function. This function must be defined in the following form (obviously the name can change) void sample_Effect(int stepno, RappaFrameInfo *rfip, VisData *pVD) { /* your code here */ } The stepno parameter is which step rappa would like you to render. As a general rule in a transition effect with stepno == 0 the rendered frame should be close to the first input frame while with stepno == rpi.nsteps - 1 the rendered frame should be close to the 'rpi.end_input' input frame. Similarly with a filter effect, if the stepno == 0 and stepno == rpi.nsteps - 1 frames look similar to the input frames and the filter is applied more the futher you are from either end, the smoother the application of the filter will appear. The pVD parameter (of type VisData *) is the data passed to sonique vis plugins by sonique and can be used to make a transition or filter responsive to the music. I will not cover this in this document. The most important parameter is rfip (of type RappaFrameInfo *, as definied in rappapi.h). It contains all the other information required to generate the transition/filter effect. The elements of the structure that this points to are as follows width The width of the screen. height The height of the screen. pitch The number of bytes to add per line to get the start of that scan line. This value may be, and indeed usually is, negative. This means to access the point (x,y) in a frame 'frame' you would need to use frame[y * pitch + x] and not frame[y * width + x]. output This is a pointer to the first line of the output frame. The data stored in it is one long per pixel in 32 bit color. The values of these pixels are of the form 0x00RRGGBB. You can access the red, green and blue components of any of these values using the following defines #define GET_RED(pixel) (((pixel) >> 16) & 0xff) #define GET_GREEN(pixel) (((pixel) >> 8) & 0xff) #define GET_BLUE(pixel) ((pixel) & 0xff) #define SET_RED(pixel, red) \ (pixel) = (((pixel) & 0xff00ffff) | (((red) & 0xff) << 16)) #define SET_GREEN(pixel, green) \ (pixel) = (((pixel) & 0xffff00ff) | (((green) & 0xff) << 8)) #define SET_BLUE(pixel, blue) \ (pixel) = (((pixel) & 0xffffff00) | ((blue) & 0xff)) Bear in mind that these become extremely inefficient if used on large numbers of pixels, but they should give you an idea of what the pixels contain and how to use them. The output frame is set to all zero when the function is called so if you retuned nothing would be displayed. This is where you render what you want to appear. inputs[..] This is an array of input frames. These are in the same form as the output frame but contain the output from each of the vises that are running, as defined by the ninputs. The inputs outside the range of 0..ninputs - 1 are not defined. So, given this, how do you create a simple effect? Let's do an effect that just fills from the top to the bottom of the screen with more and more of the new vis being display each frame. Starting with the structure and other information as definied earlier, we can do this as follows void sample_Effect(int stepno, RappaFrameInfo *rfip, VisData *pVD) { int switch_pos; int y; // This is the scan line after which we will switch back // to the original vis switch_pos = rfip->height * stepno / rpi.nsteps; // First draw the top in the new vis for (y = 0; y < switch_pos; y++) memcpy(&(rfip->output[y * rfip->pitch]), &(rfip->inputs[1][y * rfip->pitch]), rfip->width * sizeof(unsigned long)); // Now draw the rest in the old vis for (; y < rfip->height; y++) memcpy(&(rfip->output[y * rfip->pitch]), &(rfip->inputs[0][y * rfip->pitch]), rfip->width * sizeof(unsigned long)); } So, our entire file is as appears below. ------------------------------------------------------------------------------ #include #include "vis.h" #include "rappapi.h" // Forward declaration void sample_Effect(int stepno, RappaFrameInfo *rfip, VisData *pVD); RappaPluginInfo rpi = { 1, // Version - currently 1 64, // Some number of steps 2, // Number of inputs 1, // End input, the second one sample_Effect, "Sample", // The display name "Sample" // The internal name }; extern "C" DLLEXPORT RappaPluginInfo * QueryRappaPlugin(void) { return &rpi; } void sample_Effect(int stepno, RappaFrameInfo *rfip, VisData *pVD) { int switch_pos; int y; // This is the scan line after which we will switch back // to the original vis switch_pos = rfip->height * stepno / rpi.nsteps; // First draw the top in the new vis for (y = 0; y < switch_pos; y++) memcpy(&(rfip->output[y * rfip->pitch]), &(rfip->inputs[1][y * rfip->pitch]), rfip->width * sizeof(unsigned long)); // Now draw the rest in the old vis for (; y < rfip->height; y++) memcpy(&(rfip->output[y * rfip->pitch]), &(rfip->inputs[0][y * rfip->pitch]), rfip->width * sizeof(unsigned long)); } ------------------------------------------------------------------------------ Compile this into a .DLL file and rename the file with the extension of .REP (Rappa Effects Plugin) then copy the file to the same directory that has the rappa.svp file inside your sonique vis directory. When you next start the rappa vis it will include this effect and use it randomly as with all the other effects. If you are testing, you can turn on the option NoInternalTranslations=1 in the [rappa] section of the VIS.INI file in the sonique directory. This will leave you only with a basic show-the-vis 'filter' which is used most of the time between transitions. A little more information to help you understand what is going on with rappas choice of effects. The effects are broken into two categories, those with 1 input and those with more than one input. This is effectively filters and transitions. It will always run a filter then a transition then a filter then transition etc. Each of these is chosen randomly from the available list. There is a special case for filters, however, in that half the time rappa will use the basic show-the-vis filter, the other half of the time it will randomly chose from all those available (which includes the basic one), so you should see an unchanged vis much of the time. Hope this all helps, if you create a transition, drop me a line and I'll put it up for others to download. Mykel -- mykel@earthling.net http://members.xoom.com/mykelsonique