Recent

Author Topic: Help with Alsa C conversion.  (Read 8702 times)

AL

  • Sr. Member
  • ****
  • Posts: 264
Help with Alsa C conversion.
« on: July 28, 2020, 08:07:43 pm »
I am trying to convert a C or C++ code to Lazarus.  I am using fpAlsa.
So far I have a working code to list the PCM devices except that at the end of the list I do not detect the end of the array and this generate a SIGSEV error.

Here is the original C code:
Code: C  [Select][+][-]
  1. char **hints;
  2. /* Enumerate sound devices */
  3. int err = snd_device_name_hint(-1, "pcm", (void***)&hints);
  4. if (err != 0)
  5.    return;//Error! Just return
  6.  
  7. char** n = hints;
  8. while (*n != NULL) {
  9.  
  10.     char *name = snd_device_name_get_hint(*n, "NAME");
  11.  
  12.     if (name != NULL && 0 != strcmp("null", name)) {
  13.         //Copy name to another buffer and then free it
  14.         free(name);
  15.     }
  16.     n++;
  17. }//End of while
  18.  
  19. //Free hint buffer too
  20. snd_device_name_free_hint((void**)hints);
  21.  
  22.  

My translation is this:

Code: Pascal  [Select][+][-]
  1. Procedure ListDevices ;
  2. Var Hints : PPPointer ;
  3.     err,i   : integer;
  4.     n     : PPointer ;
  5.     Name,Desc,IOID :Pchar ;
  6.  
  7. begin
  8.  
  9.   new(Hints);
  10.   err := snd_device_name_hint(-1,'pcm', Hints) ;
  11.   if err <> 0 then begin
  12.     ShowMessage('Error accessing devices, Error:'+ inttoStr(err));
  13.     exit ;
  14.   end;
  15.   showMessage('No Error in hint');
  16.  
  17.   n := Hints^  ;
  18.  
  19.    try
  20.     try
  21.      while (n <> nil)    do begin
  22.         name := snd_device_name_get_hint(n^,Pchar('NAME')) ;
  23.         desc := snd_device_name_get_hint(n^,'DESC') ;
  24.         IOID := snd_device_name_get_hint(n^,'IOID') ;
  25.        If IOID <> '' then
  26.           if IOID = 'Input' then
  27.                Showmessage(string(Name) + ' | ' + String(desc) + ' | ' + string(IOID)) ;
  28.  
  29.       Inc(N) ;
  30.    //If snd_device_name_get_hint(n^^,Pchar('NAME')) ='' then break;
  31.       end;
  32.   except
  33.     on E: Exception do
  34.         ShowMessage(E.Message);
  35.    end;
  36.  
  37.   finally
  38.   showmessage('end of List') ;
  39.   end;
  40.  
  41.   err := snd_device_name_free_hint (hints^) ;
  42.  
  43.   if err <> 0 then begin
  44.     ShowMessage('Error freeing hints, Error:'+ inttoStr(err));
  45.     exit ;
  46.   end;
  47.     showMessage('No Error in free');
  48. end;                  
  49.  

The types from fpAlsa are :
PPointer            = ^Pointer;
  PPPointer           = ^PPointer;

My problem is that while (n <> nil)  is never detected.
Is there a mean to detect the end of this "C" array of pointer?

Thanks.

     
Laz 3.1, fpc 3.2.2, Win10
Laz 3.1  fpc 3.2.2, MacOS Monterey running on VMWare/Win 10
Laz 3.1  fpc 3.2.2 Ubuntu 20.04

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Help with Alsa C conversion.
« Reply #1 on: July 28, 2020, 08:19:29 pm »
Code: C  [Select][+][-]
  1. while (*n != NULL) {
dereferences n, so the pascal code must be:
Code: Pascal  [Select][+][-]
  1.      while (n^ <> nil)

PS: your code produces a bunch of memory leaks, try sticking to the c code, because it doesn't

AL

  • Sr. Member
  • ****
  • Posts: 264
Re: Help with Alsa C conversion.
« Reply #2 on: July 28, 2020, 10:56:54 pm »
Code: C  [Select][+][-]
  1. while (*n != NULL) {
dereferences n, so the pascal code must be:
Code: Pascal  [Select][+][-]
  1.      while (n^ <> nil)
I was sure I tried that, but evidently not.  It does work now.  Thank you

Quote
PS: your code produces a bunch of memory leaks, try sticking to the c code, because it doesn't
Sure will do!  :-)
Laz 3.1, fpc 3.2.2, Win10
Laz 3.1  fpc 3.2.2, MacOS Monterey running on VMWare/Win 10
Laz 3.1  fpc 3.2.2 Ubuntu 20.04

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11448
  • FPC developer.
Re: Help with Alsa C conversion.
« Reply #3 on: July 28, 2020, 11:14:19 pm »
Code: C  [Select][+][-]
  1. char **hints;
  2.  
  3. int err = snd_device_name_hint(-1, "pcm", (void***)&hints);
  4.  

Note that a double pointer is declared, but the address is typecast (to triple void pointer) and passed to the function. Effectively this is like passing a double pointer as VAR.

If the header is the same as the C (triple pointer), then your code is wrong. If your code has an attempt to ease with a VAR hints :ppchar or var hints:ppointer  declaration, then its is ok.

[/code]
« Last Edit: August 07, 2020, 03:55:40 pm by marcov »

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: Help with Alsa C conversion.
« Reply #4 on: July 29, 2020, 01:45:18 am »
If I understand the C code correctly, the first function is returning an allocated pointer from the C function so this means you do not use NEW to create it because at the end it is calling another C function to free the resources.

 So all that is needed is a double pointer..

 PPounter should be ok and the first initial call defines it using the @Hint;

 It will store a pointer value to be used indirectly from which the compiler should be able to translate it.

The only true wisdom is knowing you know nothing

AL

  • Sr. Member
  • ****
  • Posts: 264
Re: Help with Alsa C conversion.
« Reply #5 on: July 29, 2020, 02:09:53 am »
The translated procedure in fpAlsa use a PPPointer as parameter for hints.
So I have no choice but to use that.
Tried without a New and got a sigsev.

As I said, it is working fine now.
Thanks

Laz 3.1, fpc 3.2.2, Win10
Laz 3.1  fpc 3.2.2, MacOS Monterey running on VMWare/Win 10
Laz 3.1  fpc 3.2.2 Ubuntu 20.04

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Help with Alsa C conversion.
« Reply #6 on: July 29, 2020, 02:57:37 am »
During my last post I didn't have had much time at hand, but now I have, here is my translation:
Code: Pascal  [Select][+][-]
  1. var
  2.   hints, n: PPChar; // the same as char ** in C
  3.   err: Integer;
  4.   rawName, rawDesc, rawIOId: PChar;
  5.   name, desc, IOId: String;
  6. begin
  7.   err := snd_device_name_hint(-1, "pcm", PPPointer(@hints)); // note that a pointer to a PPChar is a PPPChar, which can be casted to PPPointer
  8.   if err <> 0 then
  9.   begin
  10.     Exit; // you probably want to raise an exception here
  11.   end;
  12.   try
  13.     n := hints;
  14.     while Assigned(n^) do // same as n^ <> nil, I just like the Assigned syntax more
  15.     begin
  16.       // recieve PChar data
  17.       rawName = snd_device_name_get_hint(n^, 'NAME');
  18.       rawDesc = snd_device_name_get_hint(n^, 'DESC');
  19.       rawIOID = snd_device_name_get_hint(n^, 'IOID');
  20.  
  21.       // copy to pascal string to make handling easier
  22.       name := rawName;
  23.       desc := rawDesc;
  24.       ioid := rawIOID;
  25.  
  26.       // After we ported these strings to pascal we can free the memory allocated as PChar
  27.       // Probably this requires the use of the C memory manager (unit cmem) or the use of a specialized function provided by the lib
  28.       // simply by the names I would think that this is probably snd_device_name_free_hint(PPointer(@rawName)); but thats just a guess
  29.       // but I'm pretty sure the following is pretty broken without a c memory manager
  30.       // you should probably also check if the pointer is already nil so you don't try to free nil
  31.       FreeMem(rawName);
  32.       FreeMem(rawDesc);
  33.       FreeMem(rawIOID);
  34.  
  35.       if (Name.Length > 0) and (Name <> 'null')
  36.       and (Desc.Length > 0) and (Desc <> 'null')
  37.       and (IOID.Length > 0) and (IOID <> 'null') then
  38.       begin
  39.           // do whatever you want here
  40.       end;
  41.       Inc(n);
  42.     end; // While
  43.   finally
  44.     //Free hint buffer too
  45.     snd_device_name_free_hint(PPointer(hints));
  46.   end;
  47. end;

Note, rather than
Code: Pascal  [Select][+][-]
  1. n := hints;
  2. while Assigned(n^) do
  3. begin
  4.  ...
  5.  Inc(n);
  6. end
you could use an integer variable i like this:
Code: Pascal  [Select][+][-]
  1. i := 0;
  2. while Assigned(hints[i]) do
  3. begin
  4.  ...
  5.  Inc(i);
  6. end
Unless you are in mode Delphi, there you can't access Pointers like arrays (well you can but only primitive types like PByte)
« Last Edit: July 29, 2020, 03:07:04 am by Warfley »

TRon

  • Hero Member
  • *****
  • Posts: 2506
Re: Help with Alsa C conversion.
« Reply #7 on: July 29, 2020, 04:24:21 am »
After we ported these strings to pascal we can free the memory allocated as PChar
cmem.free
Quote
Probably this requires the use of the C memory manager (unit cmem) or the use of a specialized function provided by the lib simply by the names I would think that this is probably snd_device_name_free_hint(PPointer(@rawName));
afaik snd_device_name_free_hint() should only be used for the hints list. I've never seen it used for anything else.

Quote
you should probably also check if the pointer is already nil so you don't try to free nil
Definitely.

AL

  • Sr. Member
  • ****
  • Posts: 264
Re: Help with Alsa C conversion.
« Reply #8 on: July 29, 2020, 05:06:00 am »
Thanks, will try that tomorrow when I get a minute.
Laz 3.1, fpc 3.2.2, Win10
Laz 3.1  fpc 3.2.2, MacOS Monterey running on VMWare/Win 10
Laz 3.1  fpc 3.2.2 Ubuntu 20.04

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Help with Alsa C conversion.
« Reply #9 on: July 29, 2020, 01:49:33 pm »
cmem.free

It should be noted that for using cmem, it must be the very first unit in the uses clauses auf your lpr file, because it registers a new memory manager and other units might have initialization blocks so the cmem unit  needs it's initialization to be executed before any other

MarkMLl

  • Hero Member
  • *****
  • Posts: 6685
Re: Help with Alsa C conversion.
« Reply #10 on: July 29, 2020, 02:20:21 pm »
cmem.free

It should be noted that for using cmem, it must be the very first unit in the uses clauses auf your lpr file, because it registers a new memory manager and other units might have initialization blocks so the cmem unit  needs it's initialization to be executed before any other

Followed immediately by HeapTrc if necessary. This is the sort of thing that's worked for me in the past:

Code: [Select]
(* Note manual addition of cmem below, this is required to allow strings and    *)
(* objects to be shared/transferred between the main program and a shared       *)
(* library. Note also relative position of HeapTrc, if cmem is used it is not   *)
(* possible to specify this at the project level (i.e. use FPC's -gh option).   *)

uses
{$ifdef USE_CMEM }
  cmem, {$ifdef USE_HEAPTRC } HeapTrc, {$endif }
{$endif }
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, UnyokedFrontendCode, AlsaMidi, DeviceSelectCode, EventCountersCode
{$ifdef USE_STATIC } , Backend {$else } , DynaMod, BackendDynamic,
Screensaver {$endif USE_STATIC } ;

That was a rather convoluted project which explicitly used cmem so that menus etc. could be copied from a backend shared object plugin to the frontend program. I'm not saying it's perfect, just that it worked for me with ALSA etc.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11448
  • FPC developer.
Re: Help with Alsa C conversion.
« Reply #11 on: July 29, 2020, 03:11:02 pm »
Note that CMEM is only necessary, if the libraries try to free your allocations, or you must free the libraries allocations.

In both cases, the library is defective. In normal cases this shouldn't be needed. I can't even the remember the last case that I needed it.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6685
Re: Help with Alsa C conversion.
« Reply #12 on: July 29, 2020, 03:29:50 pm »
Note that CMEM is only necessary, if the libraries try to free your allocations, or you must free the libraries allocations.

In both cases, the library is defective. In normal cases this shouldn't be needed. I can't even the remember the last case that I needed it.

Are you saying that an executable and explicit dynamically-loaded library now share the same FPC memory manager? That certainly didn't used to be the case (although I last looked at in in some depth in about 2013).

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11448
  • FPC developer.
Re: Help with Alsa C conversion.
« Reply #13 on: July 29, 2020, 03:35:43 pm »
Note that CMEM is only necessary, if the libraries try to free your allocations, or you must free the libraries allocations.

In both cases, the library is defective. In normal cases this shouldn't be needed. I can't even the remember the last case that I needed it.

Are you saying that an executable and explicit dynamically-loaded library now share the same FPC memory manager?

No. Should it ?  Both do their own allocation using (ultimately) mmap ? As long as the twain don't meet...

I could be wrong, I don't *nix that much anymore. 

PascalDragon

  • Hero Member
  • *****
  • Posts: 5469
  • Compiler Developer
Re: Help with Alsa C conversion.
« Reply #14 on: July 29, 2020, 03:52:22 pm »
Note that CMEM is only necessary, if the libraries try to free your allocations, or you must free the libraries allocations.

In both cases, the library is defective. In normal cases this shouldn't be needed. I can't even the remember the last case that I needed it.

Are you saying that an executable and explicit dynamically-loaded library now share the same FPC memory manager? That certainly didn't used to be the case (although I last looked at in in some depth in about 2013).

marcov is saying that as long as the used library does not require you to free memory passed back from the library or to free memory passed into the library then you do not need cmem. A shared memory manager is only required if memory allocated in module A is freed in module B. Using memory allocated in module A in module B has never been a problem.

 

TinyPortal © 2005-2018