uos icon indicating copy to clipboard operation
uos copied to clipboard

UOS with windows/delphi11.2

Open lucarnet opened this issue 6 months ago • 37 comments

Hello Fred, After successfully using UOS on Raspberry Pi5/Debian 12/Lazarus4.0, I need to port my project's client to Windows 11/Delphi 11.2 with UOS. However, the UOS units seem to be Lazarus-specific, such as uos_flat, etc. Are there any units available for Delphi? If not, can I easily fix the UOS units ?

Many thanks.

lucarnet avatar May 22 '25 17:05 lucarnet

I saw on the forum that you have to use uoslib instead of uos, but I can't find where or how to recompile it with fpc, as I don't have lazarus installed on w11.

lucarnet avatar May 22 '25 17:05 lucarnet

I'm progressing step by step. I found the uoslib git repository. Is it possible to describe in detail how to compile the sources for Windows 11/Delphi11.2 64bits? Thanks.

lucarnet avatar May 22 '25 17:05 lucarnet

Hello Lucarnet.

There is no Delphi version of uos, sorry.

Hum, about uoslib, it is looong time that I did not update it, I must re-jump into it, but it is a few sad to use the library for Pascal source.

But if you know well Delphi, maybe it could be possible to adapt uos code for it.

For Windows 11, only fpc console, fpGUI, MSEgui and LCL/Lazarus are supported by uos.

fredvs avatar May 22 '25 17:05 fredvs

Hello, I understand that the UOSlib sources may not be up to date and may have bugs with Windows 11/Delphi 11.2 or even Android. It's a real shame, because my speech recognition is fine with UOS on Debian 12. It contains several cross-platform units (home automation server on Debian/Lazarus, and Windows 11+Android clients with Delphi). Do you advise me against attempting a cross-compilation on Debian 12 for Windows 11 (I've never done it)? Or do you advise me, perhaps, not to use UOS for Windows 11 and perhaps Android? It's a shame because your UOS is great. I'm going to take some time to try to fix the UOS units for Delphi, but I'm not very optimistic; I'll make a decision based on the results. Sniff

lucarnet avatar May 22 '25 18:05 lucarnet

Not sure to understand, if you have a uos program/project done on Linux with Lazarus/LCL or fpGUI or MSEgui, all should be out-of-the-box if you compile the same code on Windows. All the uos/examples/ can be compiled on Unix or Windows. Android is a other story.

fredvs avatar May 22 '25 18:05 fredvs

My problem is that under Windows 11/Delphi, I call the various UOS functions(uos_CreatePlayer,...) from the uos.pas, uos_flat, and other Lazarus-specific units. So, I need either these units for Delphi, or the header files, if I use uoslib, from what I understand. But maybe I'm wrong?

lucarnet avatar May 22 '25 18:05 lucarnet

No, you cannot call uos methods from Delphi (or you must adapt uos code).

You may indeed use uoslib for Delphi, like for Java or C programs but, like said, uoslib is no more maintained. Did you try the demos and read the readme.txt of uoslib?

fredvs avatar May 22 '25 18:05 fredvs

Hello.

@perugina did a pull-request 8 years ago but but he closed it because it was not totally ok.

https://github.com/fredvs/uos/pull/18

https://github.com/fredvs/uos/pull/18/commits/5a410e783927a134090b1b36a0528e132aa0e823

Maybe you could take inspiration to make uos Delphi compatible. The code of @perugina seems ok for Delphi-Windows compiler (but not usable for fpc Unix).

fredvs avatar May 24 '25 16:05 fredvs

Hello, I created "uoslib.dll" (win64/x86_64) from Debian 12/lazarus4.0. When running under Windows 11/Delphi 11.2/W64bits , I'm having problems executing the function: -uos_loadlib (in function uos_loadlibs in file "uoslib_h_delphi") =>access violation load uoslib.dll=OK Perhaps compilation directives ("windows" or "mswindows") in source "uos_flat.pas" ? Any ideas ? Thanks.

function uos_loadlibs(const uoslibfilename, portaudiofilename, sndfilefilename, mpg123filename, Mp4ffFileName, FaadFileName, opusFileName: PAnsiChar): boolean; var loadresult : Integer; begin .... ///// load the audio libraries loadresult:= uos_loadlib(portaudiofilename, sndfilefilename, nil, nil, nil, nil); ....

lucarnet avatar May 27 '25 11:05 lucarnet

Hello, The uoslib loading error has been fixed when running W11/Delphi 11.2/W64 (pansichar casting problem). I'll continue.

lucarnet avatar May 27 '25 11:05 lucarnet

Hello, To use UOS on my project on Windows 11, I'm missing the important functions in "uoslib.dll":: uos_inputgetbuffer, UOS_GetInfoDevice, and various types.

How can I get them back? A copy/paste from the uos unit.pas from the Lazarus version with a uoslib project recompilation. Is this possible? Thanks.

procedure oWave.UosLoopProcPlayer; var arrayofint16 : array of cint16; arrayofcfloat : array of cfloat; i : integer; vtexte:string; VPHRASETMP:string; begin try // Get the status of the player : -1 => error, 0 => has stopped, 1 => is running, 2 => is paused. if uos_GetStatus(VGUos_PlayerIndex)=1 then begin // ShowPosition; oWave_refreshForm; UosShowLevel; if FTVoskRecognizer=nil then message_afficher('FTVoskRecognizer=nil') else begin FBufRcFromUos :=uos_InputGetBuffer(VGUOS_PlayerIndex, VGUOS_InIndex); aBufLen :=Length(FBufRcFromUos); if abuflen>0 then begin Vfinal := FTVoskRecognizer.AcceptWaveform(PSmallInt(FBufRcFromUos),abuflen);//Ca marche !!! .... end;

lucarnet avatar May 27 '25 16:05 lucarnet

I fear that is not possible like it is. To be usable for foreign application like C or Java, the methods exported must use the simple types of parameters: integer, float of char.

You will see that in uoslib.pas some methods are missing, mainly those that use arrays. But of course it should be updated but, sorry, it is impossible for me to re-jump into uoslib.pas before next year.

Did you read my post https://github.com/fredvs/uos/issues/60#issuecomment-2906927531 and did you try to compile the request of perugina with the Delphi compiler?

Imho, if you want to use uos for Delphi, it would be a much better way.

fredvs avatar May 27 '25 21:05 fredvs

Hello, Despite everything, I tried to create the missing functions. Indeed, I have an access violation when calling "uos_inputgetbuffer:TDarFloat" in my LoopPlayer function. I'm so close to the goal, do you have any ideas on how to achieve this and some time to dedicate? I would really appreciate it.

unit uoslib_h_delphi; interface uos_inputgetbuffer: function (PlayerIndex: cint32; InputIndex: cint32):TDArFloat;cdecl;

procedure oWaveUos.UosLoopProcPlayer; var aBufLen: integer; FBufRcFromUos: TDArFloat; // Mon buffer Vfinal:integer;//cardinal; Vsingle:cfloat;//single; vind:integer; arrayofint16 : array of cint16; arrayofcfloat : array of cfloat; i : integer; vtexte:string; VPHRASETMP:string; begin try //On verifie UOS non stoppe (car thread uos ??? // Get the status of the player : -1 => error, 0 => has stopped, 1 => is running, 2 => is paused. if uos_GetStatus(VGUos_PlayerIndex)=1 then begin // ShowPosition; oWaveUos_refreshForm; UosShowLevel; FBufRcFromUos :=TDArFloat(uos_InputGetBuffer(VGUOS_PlayerIndex, VGUOS_InIndex)); aBufLen :=Length(FBufRcFromUos); if abuflen>0 then begin if assigned(FTProcUosLoopPlayer) then begin //if FTProcUosLoopPlayer(PSmallInt(FBufRcFromUos),abuflen) if FTProcUosLoopPlayer(TSmallIntDynArray(FBufRcFromUos),abuflen) then incbuffer:=0;// Reinit debut de silence end; end; sleep(5); //Application.Processmessages; end; except on E:Exception do //message_afficher('LoopProcPlayer1:'+E.message); raise //LogFile_Ecrire_PrefEXC(nil,'LoopProcPlayer1:'+E.message); end; end;

lucarnet avatar Jun 04 '25 10:06 lucarnet

To complete. Thanks a lot.

unit uoslib_h_delphi; /// This is a uoaslib_h variant for Delphi >= 2009 /// adapted to latest version of uos (2180729) /// - PAnsiChar instead of PChar (to match single byte strings of uos) /// - stubs replacements for dynlibs and ctypes {This is the Dynamic loading version of uos library wrapper. Load uos library and friends (PortAudio, SndFile, Mpg123, SoundTouch) with uos_loadlibs() and release it with uos_unloadlibs(). With reference counter too... Fred van Stappen / [email protected] } interface //{$MODE objfpc} {$IFDEF MSWINDOWS} uses Windows; const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 4096; LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 256; type TProc = procedure of object ; cint16 = smallint; cint32 = LongInt; cdouble = Double; cfloat = Single; tlibhandle = THandle; const nilhandle : tlibhandle = 0; //Issue de uos.pas type TDArFloat = array of cfloat; TDArShort = array of cInt16; TDArLong = array of cInt32; TDArPARFloat = array of TDArFloat; TDArIARFloat = array of TDArPARFloat; PDArFloat = ^TDArFloat; PDArShort = ^TDArShort; PDArLong = ^TDArLong; type DynLibs = class public class function loadlibrary(aDllName : PAnsiChar): tlibhandle; class procedure UnloadLibrary(aLibHandle : tlibhandle); end; ... var uos_inputaddfilter: function(playerindex: longint; inputindex: longint; lowfrequency: longint; highfrequency: longint; gain: cfloat; typefilter: longint; alsobuf: boolean; loopproc: tproc): longint; cdecl; uos_inputgetbuffer: function (PlayerIndex: cint32; InputIndex: cint32):TDArFloat;cdecl; uos_inputsetfilter: procedure(playerindex: longint; inputindex: longint; filterindex: longint; lowfrequency: longint; highfrequency: longint; gain: cfloat; typefilter: longint; alsobuf: boolean; enable: boolean; loopproc: tproc); cdecl; ... libhandle: tlibhandle = 0; // this will hold our handle for the uoslib referencecounter: longint = 0; // reference counter function uos_isloaded: boolean; inline; function uos_loadlibs(const uoslibfilename, portaudiofilename, sndfilefilename, mpg123filename, Mp4ffFileName, FaadFileName, OpusFileName: PAnsiChar): boolean; // load the all the libraries (if filename = '' => do not load that library) procedure uos_unloadlibs(); implementation ... function uos_isloaded: boolean; begin result := (libhandle <> 0); end; function uos_loadlibs(const uoslibfilename, portaudiofilename, sndfilefilename, mpg123filename, Mp4ffFileName, FaadFileName, opusFileName: PAnsiChar): boolean; var loadresult : Integer; begin result := false; if libhandle <> 0 then begin inc(referencecounter); result := true; end else begin if length(uoslibfilename) = 0 then exit; libhandle := dynlibs.loadlibrary(uoslibfilename); // obtain the handle we want if libhandle <> nilhandle then begin try ... @uos_inputaddfilter :=getprocaddress(libhandle, 'uos_inputaddfilter'); @uos_inputgetbuffer :=getprocaddress(libhandle, 'uos_inputgetbuffer'); @uos_outputaddfilter :=getprocaddress(libhandle, 'uos_outputaddfilter'); .... referencecounter := 1; ///// load the audio libraries loadresult:= uos_loadlib(portaudiofilename, sndfilefilename, nil, nil, nil, nil); result := uos_isloaded; except uos_unloadlibs(); end; end; end; end;

lucarnet avatar Jun 04 '25 10:06 lucarnet

Hello.

Did you read my post: https://github.com/fredvs/uos/issues/60#issuecomment-2914077705 ?

fredvs avatar Jun 04 '25 11:06 fredvs

Yes, I've read and understood that you can't easily fix your code this year. If you think I can easily fix your code, feel free to send it. And if not, sorry for being insistent. With a heavy heart, I'm ruling out UOS for voice recognition in my multi-platform home automation project. Thanks anyway, and in any case, thank you for your responsiveness.

lucarnet avatar Jun 04 '25 11:06 lucarnet

OK but did you try to compile the pull-request of perugina? It is a fix to be able to compile uos with Delphi-compiler?

https://github.com/fredvs/uos/issues/60#issuecomment-2906927531

fredvs avatar Jun 04 '25 12:06 fredvs

Hello, I modified the sources according to Perugina's. Result: It compiles and works for voice processing using inputgetbuffer. One remaining problem is with Audio Output: the targeted thread generates an exception during the first "Tuos_Player.WriteOut" Any ideas? Thanks. PS: Modified files attached.

uos_2250416_Delphiw64.zip

lucarnet avatar Jun 06 '25 15:06 lucarnet

i t compiles and works for voice processing using inputgetbuffer.

Wow magnifique!

One remaining problem is with Audio Output: the targeted thread generates an exception during the first "Tuos_Player.WriteOut"

Could you give more detail, when the crash appears, what was it trying to do, some code welcome. Did you try to debug it?

fredvs avatar Jun 06 '25 15:06 fredvs

I'm also attaching the files for my test application...

umainform_delphi64.zip

lucarnet avatar Jun 06 '25 15:06 lucarnet

Thanks but I dont have Delphi.

fredvs avatar Jun 06 '25 15:06 fredvs

This allowed you to see the unit "uowaveuos.pas" and its function "oWaveUos_Traitement_InputMic_start" which calls "uos_Play(VGUos_PlayerIndex);". Some details that I hope will help you.

screenshot.zip

madexcept_report.zip

the thread crash: The call stack: in uos.pas:uos.tuosthread.execute

uos.tuos_player.writeout(0,1); line 10385: if (StreamIn[x2].Data.TypePut <> 1) or ( (StreamIn[x2].Data.TypePut = 1) and (StreamIn[x2].Data.Channels > 1)) then

in system:_BoundErr

:"hearing verification error" procedure _BoundErr; {$IFDEF PUREPASCAL} begin ErrorAt(Byte(reRangeError), ReturnAddress); end;

in system.classes:" Thread.FFatalException := AcquireExceptionObject;"

function ThreadProc(const Thread: TThread): Integer; var FreeThread: Boolean; {$IFDEF MACOS} pool: Pointer; {$ENDIF MACOS} begin {$IFDEF AUTOREFCOUNT} Thread.__ObjAddRef; // this ensures the instance remains for as long as the thread is running {$ENDIF} TThread.FCurrentThread := Thread; {$IF Defined(POSIX)} if Thread.FSuspended then pthread_mutex_lock(Thread.FCreateSuspendedMutex); {$ENDIF POSIX} {$IFDEF MACOS} // Register the auto release pool pool := objc_msgSend(objc_msgSend(objc_getClass('NSAutoreleasePool'), sel_getUid('alloc')), sel_getUid('init')); {$ENDIF MACOS} try Thread.FStarted := True; if not Thread.Terminated then try Thread.Execute; except Thread.FFatalException := AcquireExceptionObject; end;

lucarnet avatar Jun 06 '25 16:06 lucarnet

Pfff, not easy if I cannot test by myself. I see in your screenshot : TuosPlayer(theparent) and a message: Undeclared indentifier "theparent".

fredvs avatar Jun 06 '25 17:06 fredvs

What is already working, can you play files, does the mic work?

fredvs avatar Jun 06 '25 17:06 fredvs

Yes, the microphone works and voice recognition works fine (speech to text with Vosk). To process a wave file, I don't use uos, but a simpler TMemoryStream (function "for_speechtotext_file"). I modified your sources for Win64 by analogy with Perugina, but it's obviously not easy to understand your code for this debugging. I only note that it's a boundary overflow problem? 1)In the line of bug:

     if (StreamIn[x2].Data.TypePut <> 1) or  ( (StreamIn[x2].Data.TypePut = 1) and (StreamIn[x2].Data.Channels > 1)) then

I found the first cause of the bug, the length(streamIn)=1 and we read streamin[2], why?

Any idea?

lucarnet avatar Jun 06 '25 18:06 lucarnet

Correction: I found the first cause of the bug, the length(streamIn)=1 and we read streamin[1] (x2=1), why?

2)in the function threadExecute: What does "PlugEnabled" mean? Is it normal "not PlugEnabled" and run "WriteOut" instead of "WriteOutPLug" ?

if uosIsActif then begin if plugenabled = True then WriteOutPlug (x, x2) else// No plugin WriteOut (x, x2); end;

lucarnet avatar Jun 06 '25 18:06 lucarnet

Hello Fred, So I solved the problem with the following code, and the audio output is now fine. However, I don't understand how it works with Lazarus/Linux; probably a problem with the beginning of the index, like for a "string"? Hoping I don't encounter other problems, but I'm hopeful. Thanks for your help.

if uosIsActif then begin //=============== //Modif IVEO:Pb depassement x2=length(StreamIn) {$IFDEF FPC} if plugenabled = True then WriteOutPlug (x, x2) else// No plugin WriteOut (x, x2); {$ELSE} if plugenabled = True then WriteOutPlug (x, min(length(StreamIn)-1,x2)) else// No plugin WriteOut (x, min(length(StreamIn)-1,x2)); {$ENDIF FPC} end;

lucarnet avatar Jun 06 '25 18:06 lucarnet

What does "PlugEnabled" mean? Is it normal "not PlugEnabled" and run "WriteOut" instead of "WriteOutPLug" ?

"PlugEnabled" is set to true when a plugin was added like SoundTouch for tempo-tuning.

fredvs avatar Jun 06 '25 18:06 fredvs

However, I don't understand how it works with Lazarus/Linux; probably a problem with the beginning of the index, like for a "string"?

First of all, if it works for you c'est formidable.

Hum, about the difference of start of index in a array, I dont know, in fpc if nothing is defined, by default, imho, it begins with 0. In Delphi, I dont know, the last time I used it (deeply) it was +-30 years ago...

fredvs avatar Jun 06 '25 19:06 fredvs

About PlugEnabled, I have the "LibSoundTouch-64.dll" is in the directory plugin and i load this dll: i have the line "(uos_LoadPlugin('soundtouch', PAnsiChar(Flibsoundtouch)) = 0)", so this is not normal. Another small problem to solve, but not a priority for me now. Good WE

lucarnet avatar Jun 06 '25 19:06 lucarnet