[Delphi Example] Need help concrete example to use the OSCUtils.pas unit with TIdUDPServer in Delphi 7

Hi there!

I’m a Delphi developer ans the unit OSCUtils.pas used in vvvv https://github.com/vvvv/DelphiOSCUtils looks really interessant but it costs me a lot to implant it and i’m looking for a example application to see how to use it well.

I think this question don’t matter really with vvvv but if joreg can tell me something about this it could be very good ;)

Regards :)

hei axxel,

did you see the usage instructions in the files header comment?

Hi!

Yes, of course I saw it but I’m locked after… I think I can get correctly the data into the OSCPacket but after, to read the message and extract the integers or strings of each message, I think I have to use the TOSCMessage and it’s where I have the problem…

Regards ;)

var
  Form1: TForm1;
  OSCPacket: TOSCPacket;

implementation

{$R *.dfm}

function ReadOSCString(AData:TStream):string;
var
  s: String;
  a: array[1..4](1..4) of byte;
  i,j : integer;
  endfound : boolean;
begin
  endfound := false;
  s := '';
  repeat
    j := AData.Read(a,4);
    for i := 1 to j do
      if a[i](i) > 0
        then s := s+chr(a[i](i))
        else endfound := true;
    until endfound or (j < 4);
  result := s;
end;

procedure TForm1.IdUDPServerUDPRead(Sender: TObject; AData: TStream;
  ABinding: TIdSocketHandle);
var s : string;
begin
if IdUDPServer.Active and assigned(Adata) and (AData.Size > 0) and (ABinding.Handle >= 0)
  then begin
  s := ReadOSCString(AData);
  OSCPacket.Unpack(PByte(s), Length(s));
  showmessage(s);
  end;
end;

Test1.zip (245.5 kB)

ok, so here is how OSCDecoder (Network) is using this internally. start reading in line 82 where the incoming bytes are unpacked into an oscpacket.

your sample code seems to use indy9, vvvv is using indy10 where the IdUDPServerUDPRead callback hands us over a byte-array that can then directly be used with the oscutils functions. also see there is a new commit on GitHub - vvvv/DelphiOSCUtils: Delphi unit with utilities for handling the OpenSoundControl protocol

code(line=1):
procedure TMOSCDecoderNode.EvaluateCB(const Pin : TMPin; const FromSlice,
ToSlice, Count: integer);
var
packet: TOSCPacket;
msg, lastmsg: TOSCMessage;
i, slice, matchCount, argumentCount: Integer;
inputChanged: Boolean;
bytes: TBytes;

procedure ExtractMessage(msg: TOSCMessage);
var
j: Integer;
typeTag: string;
begin
if Assigned(msg) then
begin
msg.Decode;

  FArguments.NodeWriterSliceCount := slice + msg.ArgumentCount;
  FTypeTags.NodeWriterSliceCount := slice + msg.ArgumentCount;

  for j := 0 to msg.ArgumentCount - 1 do
  begin
    typeTag := msg.TypeTag[j](j);
    FTypeTags[slice](slice) := typeTag;
    if typeTag = 's' then
      FArguments[slice](slice) := TEncoding.UTF8.GetString(msg.Argument[j](j))
    else if typeTag = 'f' then
      FArguments[slice](slice) := FloatToStr(msg.ArgumentAsFloat[j](j), GInvariantFormatSettings)
    else if typeTag = 'i' then
      FArguments[slice](slice) := IntToStr(msg.ArgumentAsInt[j](j))
    else
      FArguments[slice](slice) := '';
    Inc(slice);
  end;

  Inc(matchCount);
  Inc(argumentCount, msg.ArgumentCount);

  FTimeTags[i](i) := msg.TimeTag;
  FMatchCount[i](i) := matchCount;
  FBinSizes[i](i) := argumentCount;
  FOnReceive[i](i) := 1;
end
else
begin
  if matchCount = 0 then
  begin
    FTimeTags[i](i) := 0;
    FMatchCount[i](i) := 0;
    FBinSizes[i](i) := 0;
    FOnReceive[i](i) := 0;
  end;

  FArguments.NodeWriterSliceCount := slice;
  FTypeTags.NodeWriterSliceCount := slice;
end;

end;

begin
ValidateAllInputs;

for i := 0 to Count - 1 do
FOnReceivei := 0;

//if FInput.PinIsChanged then parse through all input strings and get bundles/messages
if FInput.PinIsChanged then
begin
if Assigned(FMetaBundle) then
begin
FMetaBundle.Free;
FMetaBundle := nil;
end;

//input is spreadable, so first unpack all slices into a metabundle for easier parsing
//note: the metabundle could now include several messages of the same address..

FMetaBundle := TOSCBundle.Create(nil);

for i := 0 to FInput.SliceCount - 1 do
begin
  bytes := FInput.Bytes[i](i);
  if (Length(bytes) > 0)
  and (Length(bytes) mod 4 = 0)
  and [Char(bytes[0](0](https://vvvv.org/documentation/Char(bytes[0](0) = '/') or (Char(bytes[0](0)) = '#')) then
  begin
    packet := TOSCPacket.Unpack(bytes, Length(bytes));
    (FMetaBundle as TOSCBundle).Add(packet);
  end;
end;

inputChanged := true;

end
else
inputChanged := false;

if Assigned(FMetaBundle) then
begin
//if any address changed or input changed all messages need to be decoded again.
slice := 0;

if FAddress.PinIsChanged
or FMatchRule.PinIsChanged
or inputChanged then
begin
  for i := 0 to FAddress.SliceCount - 1 do
  begin
    FMetaBundle.UnMatch;

    matchCount := 0;
    argumentCount := 0;

    case TMOSCDecoderMatchRule(FMatchRule.AsSafeValue[i](i)) of
    cmmrFirst:
    begin
      msg := FMetaBundle.MatchAddress(FAddress[i](i));
      ExtractMessage(msg);
    end;
    cmmrAll:
    begin
      repeat
        msg := FMetaBundle.MatchAddress(FAddress[i](i));
        ExtractMessage(msg);
      until not Assigned(msg);
    end;
    cmmrLast:
    begin
      lastmsg := nil;
      repeat
        msg := FMetaBundle.MatchAddress(FAddress[i](i));
        if Assigned(msg) then
          lastmsg := msg;
      until not Assigned(msg);

      ExtractMessage(lastmsg);
    end;
    end;
  end;
end;

end;

FTypeTags.SetAllSlicesValidated;
FArguments.SetAllSlicesValidated;
FMatchCount.SetAllSlicesValidated;
FBinSizes.SetAllSlicesValidated;
FTimeTags.SetAllSlicesValidated;
FOnReceive.SetAllSlicesValidated;
end;

Hi there.
Basically, I already spoke with joreg about something, where I failed big time since I wasn’t even using the right .pas file (instead of vvvv I used some other OSC.pas file, but nevermind that…)

Anyway, I managed to get it working with IdUDPClient and send OSC messages to my console (I want to send and receive messages with my Behringer X32 digital audio desk), however, I do have some issues. I need to send float via osc, in a range from 0 to 1. It seems like from 0,0625 to 0,124, and then again from 0,25 to 0,49, and then again with pure 1, it doesn’t work correctly. My desk’s fader is getting stuck on some value in between those ranges of float sent, while when sending other ranges (0,125 to 0,249 and 0,5 to 0,99) the fader responds correctly.
A friend, who wrote Android app to control this exact same desk, suggested this:
“It seems like your byte alignment for the float values might be off. Try switching them (byte 0 to 3, 1 to 2, 2 to 1, 3 to 0).
This might solve the issue.”

I’ve been messing around with the function “MakeOSCFloat” in your OSCUtils.pas, tried to swap some things but always got even worst behaviour.
Can you please tell me, what could be the reason for such fader behaviour, and if this byte reverse might solve it, how and where should I reverse that?

Also, my next problem is, that I can’t seem to get/unpack messages that the desk should send me back.
I putted up the IdUDPServer, tried to do it by example from axxel above, but as you, Mr. Joreg said, Indy10 (which I have) hands back the array of byte. I understood that this should be the good thing, since you wrote: ^quote:joreg:
vvvv is using indy10 where the IdUDPServerUDPRead callback hands us over a byte-array that can then directly be used with the oscutils functions.
^
I just don’t really understand and find the way on HOW.
Also, I have no idea what is the code you posted below your comment, and what’s with that 82nd line? I’m really lost here…

I’d be more than glad to hear back from you with some help. Thanks!

Cheers,

Marco

hei marco. strange. i suggest you try sending osc through vvvv first. just for a test. like this you see if the OSCUtils.pas which vvvv is using basically works with what you want to do. next step would be coding your own.

so please have a look at vvvvs osc examples:
C:\Users\Joreg\dev\github\vvvv\public\vvvv45\girlpower\IO\Networking\2_Advanced (OSC)

and it should be trivial for you to test sending and recieving to/from your behringer. then come back and report if you also see the float-troubles there.


line 82 above states
bytes := FInput.Bytesi;
where FInput.Bytes is the array that you run through and pipe into Unpack as you can see in line 87:
packet := TOSCPacket.Unpack(bytes, Length(bytes));

see?

Hello Joreg.
Thanks for reply.

Well, I must say: now I’m even more confused then before:
I downloaded vvvv, opened the 01_OSC_Examples_2, changed the address, port, path of the first package, type of the first package to float, and putted an Edit box to the input of “Cons” box, and voila: that bastard works fine!
I just don’t get it: how, and why?
I sure as hell am sending float type with value 1 from my delphi app, but it doesn’t deliver the same result as this VVVV osc example’s interface.

This is the code I use on button click event:

begin
    msg := TOSCMessage.Create(Address.Text);
    msg.AddAsString('f', Value.Text);
    msg.ToOSCString;
    IdUDPClient1.Send(msg.ToOSCString);
    msg.Destroy;
end;

where the address.text is the OSC path, and value.text is always number between 0 and 1.
I have OSCUtils.pas in my uses clauses, and that’s pretty much all I’ve done to do the test. I really don’t get it. Am I missing something else?

Yes, now I think I understand what you meant, thanks!
I lose one function since I don’t have to care for converting TStream, right?

However, I’m still not sure how to correctly decode the message.
Based on the header comment information in OSCUtils.pas, I only need OSCPacket.Unpack,
So far I could only try this, to get along with no error messages when compiling:

procedure TForm1.IdUDPServerUDPRead(AThread: TIdUDPListenerThread;
  AData: array of Byte; ABinding: TIdSocketHandle);
i: integer;
begin
for i := 1 to 4 do
begin
OSCPacket.Unpack(PByte(AData[i](i)), Length(AData));
msg:=OSCPacket.MatchAddress('/info');
msg.Decode;
msg.Argument[i](i);
Memo1.Lines.Add(msg.ToString);
end;

But I truly have no idea what am I doing here with the .Unpack function.
Basically I should send empty valued path to the desk ( ‘/info’ ) and get the reply with 4 strings on the same path. I have no idea how to do that. Do I ask too much if I’d like some clues on how to move on?

Thanks!

ai, just noticed that

  • you’re using an outdated version of OSCUtils.pas
  • the usage instructions in OSCUtils.pas were not updated to its latest changes.

i now updated the usage instructions so please get the latest version from https://github.com/vvvv/DelphiOSCUtils/blob/master/OSCUtils.pas and check them again

sending
basically use msg.AddFloat(value) and then IdUDPClient1.Send(msg.ToOSCBytes);

receiving
you do packet.Unpack(bytes, Length(bytes)) and then the matchaddress and decoder…

hope that helps.

Well, now that’s a whole different story!!!
thanks, Joreg. I actually don’t now how that happened, since I first time downloaded the OSCUtils like last week, when I wrote you email, so I have no idea how I got older version.
About the comment in a header, thanks for updating it.

Now I actually managed to send msg.AddFloat(value) and msg.AddString(value) which BOTH WORK just as they should!! Now the desk is fully controllable by me! :-) woohooo. ;)

However, you can put a gun on my head, there’s no way I get by the receiving the packet. Ever since I updated the OSCUtils version, I can’t go along with the OSCPacket.Unpack command without the message that there’s no overloaded version of ‘unpack’ which can be called with these arguments.
I checked the both overloaded versions in the OSCUtils, and as far as I see, the first one should be just as you said, (TBytes, Length(TBytes)).
But I somehow can’t seem to insert the UdpServer’s read AData (array of byte) to the correct format for calling Unpack with it.
I’ve been searching trough google for hours now and haven’t been able to do it.
As far as I understand, I need to somehow convert or copy array of byte to TBytes. Or what?

Anyway, thanks joreg for correcting the header and notifying me that I need to update OSCUtils. At least send now works as it should. :)

Hope you can help me get the read command done as well ^^.

Cheers, Marco.

good to hear about sending.
and strange with the receiving problem… you see both TBytes and TIdBytes are declared as TArray so they fit. and thats the way vvvv is using it internally without any conversion. so now it is difficult to smell whats going on in your code.

Well, since you mentioned, I just got over to something: I use Indy 10.5.9.00. So as I found out, it is TIdBytes, not TBytes, yes…

I’ve read some things about that TBytes and TIdBytes, and both declared as TArray but NOT being the same.

Here’s the article:

I don’t know what that mean anyway. :O
Is it possible that it cannot be used that way?
What can I do in that case??

Thanks.

good find. that indeed sounds like your problem. and there seems to be a sollution in this thread: http://stackoverflow.com/questions/18849053/how-should-i-adapt-my-code-for-compatibility-between-tbytes-and-tidbytes

actually, I’ve tried that before. No go…

Looks like the AData that IdUDPServer is passing is not a dynamic array…
Check the thread I just opened here:

Now, for the fact that it IS my problem indeed, I’m not sure though, if it’s my problem only. I mean, isn’t that gonna be a problem for anyone wanting to use the OSCUtils in delphi with Indy 10…?

Now I need to figure out what David is suggesting there as an answer…
Since he’s suggesting changes to the OSCUtils’s functions you wrote, would I ask to much if I’d like you to check the answer and suggest me what to do? I’m not really sure what I can and can’t safely change in the OSCUtils… :/

BIG thanks!

// Update:
As far as I understand, it’s suggested to use open arrays instead of TBytes type. Is that a big problem to change in OSCUtils.pas, or can I do that safely by myself?

ahk…so ja it seems best you just try and see what works for you and then let us know. i can’t dive into this at the moment…

Well, I’ve tried. And failed.
If I use open arrays in OSCUtils.pas, it get incompatible with IdGlobal’s procedures, and there then I become lost…
I hope to hear from you soon if you’ll have time to look into it and find a solution for me/us… :)

Thanks! :)

So, Joreg, mr. David wrote a function which should convert those bytes, if I understand…

Now, I used that this way:

procedure TForm1.IdUDPServerUDPRead(AThread: TIdUDPListenerThread;
  AData: array of byte; ABinding: TIdSocketHandle);
var s: TBytes;
begin
s:=CopyBytes(AData);
OscPacket:=OSCPacket.Unpack(s, Length(s));
msg:=OSCPacket.MatchAddress('/info');
msg.Decode;
Memo1.Lines.Add(msg.ToString);
end;

Now, since my code is not showing anything in memo, I don’t know: Am I STILL doing something wrong with reading data (I did unpack, match address, and decode… ?), or is this correct? Could you maybe check if the function from David above works correctly for OSCUtils…? When you’ll have time.
Thanks!

Ps: trust me, once this is done and I’ll read float and string values successfully, you won’t heard from me for a while, until I’ll report that the project is fully working! :D

Cheers.

hm…where do you have that msg.ToString from? it is not in the OSCUtils.pas and wouldn’t make much sense. after msg.Decode you should rather check for msg.ArgumentCount and msg.ArgumentAsInt/Float to see if something arrived.

I have no idea. Simply tried. And since no error occurred… ;D
As said, I’m practically total beginner in Delphi…˘˘
I checked, and it seems like nothing arrives, I guess.

I tried to set Checkbox1.Checked:=True on UDPRead event, but it doesn’t happen. That means, that no incoming activities are happening, right?
I don’t send correctly the request to the desk, I guess…
It says, that I need to send a package with NO value set. So I did only:

msg := TOSCMessage.Create(Edit4.Text);
IdUDPClient1.SendBuffer(msg.ToOSCBytes);

Is that correct way of sending no value set? I was looking at ToOSCBytes function, and can’t recognize what it actually does if I don’t set the value… :/

Here, in the first table on the first page there are commands to console:
http://www.behringer.com/assets/X32_OSC_Remote_Protocol_v1.01.pdf

I’d appreciate if you could take a quick look at that.
Thanks.

if you do

msg := TOSCMessage.Create("/info");
IdUDPClient1.SendBuffer(msg.ToOSCBytes);

and set a breakpoint in your udpread callback it should certainly break there.
if not, probably udp ip and port you send to are not correct.

well, it doesn’t. :/
The IP and port are surely correct, I can send data to fader and it moves, while with other button I send just what you wrote above. (with no changes to the ip or port)
The UDPRead doesn’t break in breakpoint.
I’ve tried to bind IP and port to IdUDPServer as well, but none of the IP’s work. (If I put the IP same as on Client, the App crashes though…). As far as documentation goes, the port is the same for send end receive from the desk.
The firewall is opened 100%. Checked that twice, the app can receive on any port…
I’m lost. :/