How do I handle an int** in Ada?

Devsman

I'm trying to call SDL_LoadWAV using a pre-made binding to the C library from which it comes. SDL_LoadWAV is just a wrapper for SDL_LoadWAV_RW:

function SDL_LoadWAV
 (file      : C.char_array;
  spec      : access SDL_AudioSpec;
  audio_buf : System.Address;
  audio_len : access Uint32) return access SDL_AudioSpec
is
begin
  return SDL_LoadWAV_RW
      (SDL_RWFromFile (file, C.To_C ("rb")),
       1,
       spec,
       audio_buf,
       audio_len);
end SDL_LoadWAV;

Here is the prototype of the function in C:

SDL_AudioSpec* SDL_LoadWAV_RW(SDL_RWops*     src,
                          int            freesrc,
                          SDL_AudioSpec* spec,
                          Uint8**        audio_buf,
                          Uint32*        audio_len)

(See here for more information)

Now as you can see, it passes a Uint8 (unsigned 8-bit integer) array by reference, in the form of a Uint8**. This is causing me a great bit of vexation. Here is the appropriate binding:

function SDL_LoadWAV_RW
 (src       : access SDL_RWops;
  freesrc   : C.int;
  spec      : access SDL_AudioSpec;
  audio_buf : System.Address;
  audio_len : access Uint32) return access SDL_AudioSpec;
pragma Import (C, SDL_LoadWAV_RW, "SDL_LoadWAV_RW");

As you can see, the binding maps the Uint8** to a System.Address. I've tried a couple of tricks to get that data where I want it to go, but nothing seems to work. Right now, my code looks like this (it has a few custom types and exceptions in it):

type Music is new Resource with
record
    --Id : Integer; (Inherited from Resource)
    --Filename : Unbounded_String; (Inherited from Resource)
    --Archive_Name : Unbounded_String; (Inherited from Resource)
    --Zzl_Size : Integer; (Inherited from Resource)
    Audio : access SDL_AudioSpec_Access;
    Length : aliased Uint32;
    Buffer : System.Address;
    Position : Integer := 1;
end record;

overriding procedure Load(Mus : in out Music) is
    Double_Pointer : System.Address;
begin
    Log("Loading music " & To_Ada(Get_Audio_Filepath(Mus)));
    Audio_Load_Lock.Seize;
    if null = SDL_LoadWAV(Get_Audio_Filepath(Mus), Mus.Audio.all, Double_Pointer, Mus.Length'access) then
        raise Audio_Load_Failed with To_String(Mus.Filename) & "&Stack=" & Get_Call_Stack;
    end if;
    Log("Music length =" & Integer'Image(Integer(Mus.Length)));
    declare
        type Sample_Array is array(1..Mus.Length) of Uint8;
        Single_Pointer : System.Address;
        for Single_Pointer'address use Double_Pointer;
        pragma Import(Ada, Single_Pointer);
        Source : Sample_Array;
        for Source'address use Single_Pointer;
        pragma Import(Ada, Source); 
        Dest : Sample_Array;
        for Dest'address use Mus.Buffer;
        pragma Import(Ada, Dest);
    begin
        Dest := Source;
    end;
    Audio_Load_Lock.Release;
end Load;

But, like more or less everything else I've tried, I get a PROGRAM_ERROR/EXCEPTION_ACCESS_VIOLATION when the Load function is executed.

Can anyone figure out how I need to handle this System.Address? Thanks!

Simon Wright

The definition of SDL_LoadWAV_RW says

This function, if successfully called, returns a pointer to an SDL_AudioSpec structure filled with the audio data format of the wave source data. audio_buf is filled with a pointer to an allocated buffer containing the audio data, and audio_len is filled with the length of that audio buffer in bytes.

which means that the called function allocates the required memory and fills it, then returns pointers to the allocated memory and its length.

So the binding you’ve been supplied with isn’t very good Ada.

audio_buf should be an out parameter to an array of bytes, audio_len an out parameter to a Uint32.

As a demo, using this C:

#include <stdlib.h>
void get_data (char **buf, int *len)
{
  *len = 10;
  *buf = malloc(*len);
  for (int j = 0; j < *len; j++) {
    (*buf)[j] = j;
  }
}

this Ada

type Raw is array (Interfaces.Unsigned_32) of Interfaces.Unsigned_8
with Convention => C;

defines an array type (which would occupy 2^32-1 bytes if we actually declared one!), and this

type Raw_P is access all Raw
with Convention => C, Storage_Size => 0;

defines a pointer to such an array. Limiting the storage size to 0 means that we can’t say new Raw_P.

Putting these together,

with Ada.Text_IO; use Ada.Text_IO;
with Interfaces;
procedure Demo is
   type Raw is array (Interfaces.Unsigned_32) of Interfaces.Unsigned_8
   with Convention => C;

   type Raw_P is access all Raw
   with Convention => C, Storage_Size => 0;

   procedure Get_Data (In_Buffer : out Raw_P;
                       Length    : out Interfaces.Unsigned_32)
   with
     Import,
     Convention    => C,
     External_Name => "get_data";

   Allocated : Raw_P;
   Length    : Interfaces.Unsigned_32;

   use type Interfaces.Unsigned_32;
begin
   Get_Data (In_Buffer => Allocated,
             Length    => Length);
   for J in 0 .. Length - 1 loop
      Put (Allocated (J)'Image);
   end loop;
   New_Line;
end Demo;

gives a program which when run results in

$ ./demo
 0 1 2 3 4 5 6 7 8 9
$

----

Recognising that you’re probably stuck with

audio_buf : System.Address;

you could define (or use, if already defined) something like my Raw, Raw_P and say

procedure Get_Data (In_Buffer : System.Address;
                    Length    : out Interfaces.Unsigned_32)
with
  Import,
  Convention    => C,
  External_Name => "get_data";

and then use

Get_Data (In_Buffer => Allocated'Address,
          Length    => Length);

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related