Как мне обработать int ** в Аде? - PullRequest
2 голосов
/ 27 октября 2019

Я пытаюсь вызвать SDL_LoadWAV, используя предварительно сделанную привязку к библиотеке C, из которой она происходит. SDL_LoadWAV - это просто оболочка для 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;

Вот прототип функции в C:

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

(дополнительную информацию см. Здесь)

Теперь, как вы можете видеть, он передает массив Uint8 (8-разрядное целое число без знака) по ссылке в виде Uint8 **. Это вызывает у меня сильную досаду. Вот соответствующая привязка:

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");

Как видите, привязка отображает Uint8 ** в System.Address. Я попробовал несколько трюков, чтобы получить эти данные, куда я хочу, но ничего не получается. Прямо сейчас мой код выглядит следующим образом (в нем есть несколько пользовательских типов и исключений):

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;

Но, как и все более или менее все, что я пробовал, я получаю PROGRAM_ERROR / EXCEPTION_ACCESS_VIOLATION, когдавыполняется загрузка.

Может кто-нибудь выяснить, как мне нужно обрабатывать этот System.Address? Спасибо!

Ответы [ 2 ]

5 голосов
/ 27 октября 2019

определение SDL_LoadWAV_RW говорит

Эта функция, если она успешно вызвана, возвращает указатель на структуру SDL_AudioSpec, заполненную форматом аудиоданных данных источника волны,audio_buf заполняется указателем на выделенный буфер, содержащий аудиоданные, а audio_len заполняется длиной этого аудиобуфера в байтах.

, что означает, что вызываемая функция выделяет требуемую память и заполняетзатем он возвращает указатели на выделенную память и ее длину.

Таким образом, привязка, с которой вы работаете, не очень хорошая Ada.

audio_buf должна быть outпараметр в массив байтов, audio_len и out параметр в Uint32.

В качестве демонстрации, используя эту 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;
  }
}

эту Аду

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

определяет тип массива (который занимал бы 2 ^ 32-1 байта, если бы мы фактически объявили один!), А этот

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

определяет указатель на такой массив. Ограничение размера хранилища до 0 означает, что мы не можем сказать new Raw_P.

Собрав их вместе,

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;

дает программу, которая при запуске приводит к

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

----

Признавая, что вы, вероятно, застряли с

audio_buf : System.Address;

, вы можете определить (или использовать, если уже определено) что-то вроде моего Raw, Raw_P и скажите

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

, а затем используйте

Get_Data (In_Buffer => Allocated'Address,
          Length    => Length);
0 голосов
/ 27 октября 2019

SDL_LoadWAV запишет указатель (на новый буфер, выделенный SDL) в указанном вами месте, то есть значение неинициализированной переменной Double_Pointer, которая является случайным адресом -> kaboom!

Перед вызовомдля SDL_LoadWAV вам нужно иметь что-то вроде:

Double_Pointer := SDL_Buffer'Address;

, где SDL_Buffer был ранее определен так:

type Buffer_Access_Type is access all Buffer_Type;
SDL_Buffer: Buffer_Access_Type;

Согласно API, вам необходимо бесплатно SDL_Bufferпозже с SDL_FreeWAV.

...