delphi, chunks, pointers

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

I'm trying to get vst chunks working with Tobybear's delphi template, but failing miserably. Below is what I have, stripped to the basics. But I get garbage when I display the string from the chunk, and an invalid pointer operation when I free the buffer after that.

How should I be doing this? Any help greatly appreciated.

Cheers, 3

Code: Select all

APlugin=class(AudioEffectX)
...
public
  ...
  function getChunk(var data: pointer; isPreset: Boolean): longint; override;
  function setChunk(data: pointer; byteSize: longint; isPreset: Boolean): longint; override;
  ...

constructor APlugin.CreateAPlugin(audioMaster:TAudioMasterCallbackFunc);
var
  ...
  cChunk: string;
  pChunk: PChar;
begin
  inherited Create(audioMaster,knumprograms,kNumParams);
  ...
  // send chunk
  cChunk := 'Parsnip';                     //test string to store in chunk
  GetMem(pChunk, Length(cChunk));          //allocate buffer
  pChunk := StrPCopy(pChunk, cChunk);      //copy string to buffer
  setChunk(pChunk, Length(cChunk), false); //send buffer as chunk
  FreeMem(pChunk, Length(cChunk));         //deallocate buffer
  ...
end;

procedure TPluginEditorWindow.Button1Click(Sender: TObject);
var
  cChunk: string;
  pChunk: PChar;
begin
  // retrieve chunk
  GetMem(pChunk, 255);                        //allocate buffer
  Effect.getChunk(pointer(pChunk), false);    //receive chunk into buffer
  cChunk := PChar(pChunk);                    //get string from buffer
  Messagebox(0, PChar(cChunk), Nil, MB_OK);   //display string
  FreeMem(pChunk, 255);                       //deallocate buffer
end;

Post

I dunno exactly, because I cannot program Object Pascal as good, but mainly in C/C++.

Perhaps this could help you:

The data pointer as argument of the chunk funktion is defined in the VST SDK as <void/char **data>, this means a pointer to a pointer, not simply a pointer to the data.
Last edited by blümchen on Tue Aug 24, 2004 10:30 pm, edited 1 time in total.

Post

Here is the C implementation:

Code: Select all

long MyEffect::getChunk (void** data, bool isPreset)
{	
        // we simply transmit the pointer to our programs
	*data = programs;
	return sizeof(programs);
}
The given argument is a pointer variable which must be filled with an address.
You fill the given variable with the pointer to your data (with the address of your binary chunk data) and return the size of this data.

As far as I see, this is not the case in your Object Pascal implementation. This seems to be a simple pointer in your function declaration.
Last edited by blümchen on Tue Aug 24, 2004 11:18 pm, edited 2 times in total.

Post

By the way:

The functions getChunk(...) and setChunk will be called by the host application exclusively, not from inside the plugin itself. You only have to implement it as (inherited) members of your class.

The host calls getChunk() on request for saving programs and setChunk() on request for loading programs by the user or on startup (complete song recall).

You should not call it anywhere, unless you write an host application. So your example code above is completely questionary.

Post

Thanks for replying!

OK, I understand your comment about pointers to pointers. But I think I need to ask some more specific advice on how to do this in delphi from someone here, if i can. I'm definitely a beginner with memory and pointers.

I guess if the host is supposed to call setChunk(), then when I call it, my chunk is not being sent to the host. All I'm doing is calling my own code. Huh.

So is it supposed to work more like this:
Call programsAreChunks(true) in constructor;
Implement getChunk() to copy all my float parameters plus private data together into one chunk, and send it when isPreset is true;
Implement setChunk() to get the chunk, split it into float parameters and private data, set all the float parameters with setParameter(), when isPreset is true;
Do default behaviour in getChunk() and setChunk() when isPreset is false;
Is there then no need to implement SetProgram()?
Do I still call setParameterAutomated as normal from my UI knobs etc?

I admit to getting confused about this. Any clarification or examples you or anyone could give will be immensely helpful :)

Cheers, 3

/edit: ok, you did clarify some more while I was taking a long time to type that reply :)

Post

Basically right.

Normally you use the formatless chunk mechanism only, if you have data which cannot represented by the default VST parameter implementation.

Otherwise the default mechanism works good enoughth.

If your plugin implements automate-able parameters, the setParameterAutomated() works like before.

It depends on your congreete implementation what happens on setParameter() and setProgram(). You must implement this.

A chunk is not neccessairly only a single program, but can consist of many programs and other data. But only data, that YOU have defined. So you have to make sure that there the right action on setProgram() is performed.

Some hosts call setProgram(curProgram) and / or a setParameter()loop automatically after calling setChunk(), asuming the plugin has to be updated. Other hosts don't call it automatically.

The savest way is to ensure, that all parameters are updated right by the plugin itself, after a new patch was loaded ( on setChunk() ).

How you organize and encode the data in this formatless binary block of data is completely yours.

If your plugin has more than one program, you have to implement anything inside setProgram(), for example to update all the parameters to reflect the new program.

getChunk(), setChunk() will NOT be called by changing a single program, but only on loading and saving *.fxb files and/or on song recall. In this case You cannot longer save single patches with the default VST methods, unless you implemet you own mechanisms to provide (re-implement) this feature...

.
Last edited by blümchen on Wed Aug 25, 2004 1:25 am, edited 2 times in total.

Post

My tip:

I organize all the chunk data inside a structure or class "Programs". All modifications are updated in realtime by other functions, so the data is always actual.

If getChunk() is called I only have to fill out the pointer (address of the class instance) and to return the size of it. The host then copies this block of memory.

On setChunk() I only check the size and signature and than load the entire data block completely into the Program class instance. After this I call internally setProgram(), which sets all my parameters in loop and eventually updates all the other data.

On setProgram(), I call setParameter() in a loop by calling up the data from my Programs calss (which always is a "bank".

For saving and loading single patches (if there are any), I implement my own methods and provide file requesters to load and save my own file format.

ps: I am not so sure what the <bool isPreset> argument exactly means, because I never considered this until now.
Last edited by blümchen on Wed Aug 25, 2004 1:33 am, edited 2 times in total.

Post

By the way:

Is there anybody, who has information about the size limitations for chunks, the host application stores with the song setup (i.e. Cubase) ?

Post

thanks 007, this has been a big big help for my understanding :)

Post

Hi,

I featured out the meaning of the <isPreset> argument while working on the CreakBox code.

This flag (if (isPreset == true)) is intended to indicate, that the current handled chunk is a single patch. Only stupid named.
Otherwise it is a complete sound bank requested.

Note: This was discovered on Grandmothers reference host (Cubase). There may be some hosts out, which don't cosider this flag with chunk based plugins.

.

Post

agent 007 wrote:Here is the C implementation:

As far as I see, this is not the case in your Object Pascal implementation. This seems to be a simple pointer in your function declaration.
The keyword "var" in Object Pascal means pointer to variable. So if the parameter is pointer and declared with "var" then it is pointer to pointer.
jouni - www.markvera.net - Stardrive Studio - Orionology

Post

Thanx for teaching me.
So it should work right for threetwosevensixseven.

Post

Yes indeed, I've now got it working well. I was having some silly n00b problems because my chunk included some object references, but now I'm passing in a record structure it all works nicely.

A kind person pointed this out to me on one of the Delphi newsgroups. He also had a concern that the sdk would be using the thiscall calling convention, and that Delphi wouldn't be handling that properly. But it apparently is.

Good stuff about the ispreset flag. I'll see how many hosts I can get fxp saving and loading working in.

Thanks 007 again and Mark :)

Post

Hi, I know this is an old thread but I'm going down the same path and need a working example of how to use chunks in a VST to save and retrieve plain text. If anyone has a Delphi example of that it would be very helpful if you could post it. If I can just get it working at all I can go from there. So far no luck with the example in Tobybear's VST Chunks PDF file.

Post

This is an excerpt from my Diodow synthesizer (developed in Delphi). I am using ShortString type for file path here. If you need strings with more than 255 characters you can use array [..] of Char or whatever ...

Code: Select all

interface

type

  XSynthesizer=class(AudioEffectX)
  private
    // ...
  public
    // ...
    function GetChunk(var Data: Pointer; IsPreset: Boolean=False): VstInt32; override;
    function SetChunk(Data: Pointer; ByteSize: VstInt32; IsPreset: Boolean=False): VstInt32; override;
  end;

  XSynthesizerData=record
    // ...
    PatchFile: string[255];
  end;

// ...

implementation

// ...

function XSynthesizer.GetChunk;
begin
  Result:=SizeOf(XSynthesizerData);
  GetMem(Data,Result);
  with XSynthesizerData(Data^) do
  begin
    // ...
    PatchFile:=...MultiPatch.PatchFile
  end
end;


function XSynthesizer.SetChunk;
begin
  Result:=1;
  if not Assigned(Data) then Exit;
  // ...
  try
    if ByteSize<>SizeOf(XSynthesizerData) then
      ErrorMsg(DiodowDll+#13#13+'Different synthesizer data format!')
    else
    begin
      // ...
      with XSynthesizerData(Data^) do
      begin
        // ...
        ...MultiPatch.PatchFile:=PatchFile
      end
    end;
    // ...
  finally
    // ...
  end
end;

// ...

Post Reply

Return to “DSP and Plugin Development”