



  function ALErrorStr(e: ALenum): WideString;
  begin
    case e of
      AL_NO_ERROR: Result:= 'AL_NO_ERROR';
      AL_INVALID_NAME: Result:= 'AL_INVALID_NAME';
      AL_INVALID_ENUM: Result:= 'AL_INVALID_ENUM';
      AL_INVALID_VALUE: Result:= 'AL_INVALID_VALUE';
      AL_INVALID_OPERATION: Result:= 'AL_INVALID_OPERATION';
      AL_OUT_OF_MEMORY: Result:= 'AL_OUT_OF_MEMORY';
    else
      Result:= IntToHex(e, 4) + 'h' + RuEn(' (неизвестная ошибка)','(unknown error)');
    end;
  end;

  function ALCErrorStr(e: ALCenum): WideString;
  begin
    case e of
      ALC_NO_ERROR: Result:= 'ALC_NO_ERROR';
      ALC_INVALID_DEVICE: Result:= 'ALC_INVALID_DEVICE';
      ALC_INVALID_CONTEXT: Result:= 'ALC_INVALID_CONTEXT';
      ALC_INVALID_ENUM: Result:= 'ALC_INVALID_ENUM';
      ALC_INVALID_VALUE: Result:= 'ALC_INVALID_VALUE';
      ALC_OUT_OF_MEMORY: Result:= 'ALC_OUT_OF_MEMORY';
    else
      Result:= IntToHex(e, 4) + 'h' + RuEn(' (неизвестная ошибка)','(unknown error)');
    end;
  end;

  var
    lsoad_device: pointer;
    lsoad_devname: ansistring;

  const
    alcontextattribs: array[0..4] of ALCint = (ALC_STEREO_SOURCES, 1,

      {Q: I do not want OpenAL mixer thread, I want mixer working from my own thread.
       A: It’s very easy : use attribute ALC_SYNC set in AL_TRUE. Call alcProcessContext(context); from your worker thread.}
      ALC_SYNC, AL_TRUE,

      0);

  constructor TOpenALSoundMan.Create;
  var
    DevErr: WideString = '';
    error: ALCenum;
    alDevName,
    alDefDevName,
    alActDevname: AnsiString;
    sbufs, issync, freq: ALCint;
    issyncn: WideString;
    i: integer;
    e: ALenum;

    procedure OpenDevice(name: AnsiString);
    begin
      if MotherState.DebugMode then AddLog('  Opening device "%0"...', [name]);

      device:= alcOpenDevice(PcChar(name));
      if MotherState.DebugMode then
        if Assigned(device) then AddLogOk
                            else AddLogComment('FAILED.');
    end;
  begin
    try
      InitOpenAL;
      alDevName:= Config.Str['openal', MotherState.OstID + '_device_name'];
      if MotherState.DebugMode then begin
        if alDevName = '' then AddLog('  No device specified in config.')
                          else AddLog('  Device "%0" is specified in config.', [alDevName]);
        AddLog('  Default device name is "');
      end;

      alDefDevName:= PCharToString(PChar(alcGetString(nil, ALC_DEFAULT_DEVICE_SPECIFIER)));
      if MotherState.DebugMode then AddLogComment(alDefDevName + '"');

      if alDevName <> '' then OpenDevice(alDevName);
      if not Assigned(device) then OpenDevice('');

      if not Assigned(device) then begin
        if alDevName = ''
          then Die(RuEn(
                   'Не удалось открыть устройство по умолчанию "%0".',
                   'Unable to open the default device "%0".'),
                   [alDefDevName])
          else Die(RuEn(
                   'Не удалось открыть'#10#13
                 + '  ни устройство "%0",'#10#13
                 + '  ни устройство по умолчанию "%1".'
                  ,'Unable to open'#10#13
                 + '  neither the device "%0"'
                 + '  nor the default device "%1".'),
                   [alDevName, alDefDevName]);
      end;

      if MotherState.DebugMode then AddLogOk;

      alActDevname:= PCharToString(PChar(alcGetString(device, ALC_DEVICE_SPECIFIER)));
      if (alActDevName = '') and (alDevName = '') then alActDevName:= alDefDevName;

      if (alDevName <> '') and (alActDevName <> alDevName) then AddWarning(
        'OpenAL',
        RuEn('Вместо устройства "%0" было открыто "%1"',
             'Device "%1" whas been opened'#10#13'  instead of "%0".'),
             [alActDevName, alDevName]);


      context:= alcCreateContext(device, @alcontextattribs[0]);
      if not Assigned(context) then Die(RuEn(
                           'Не удалось создать контекст','Context not created'));
      alcMakeContextCurrent(context);
      alcGetIntegerv(device, ALC_STEREO_SOURCES, sizeof(ALCint), @sbufs);
      if sbufs < 1 then Die(RuEn(
               'Не поддерживаются стерео источники','No stereo sources support'));
      alcGetIntegerv(device, ALC_FREQUENCY, sizeof(ALCint), @freq);
      alcGetIntegerv(device, ALC_SYNC, sizeof(ALCint), @issync);
      if issync = AL_TRUE then issyncn:= RuEn('да','yes') else issyncn:= RuEn('нет','no');

      AddLog(RuEn(
        'OpenAL: устройство "%0"'#10#13'  %2 Гц; синхронизация: %3; стерео источников: %1',
        'OpenAL: device "%0"'#10#13'  %2 Hz; sync: %3; stereo sources: %1'),
        [alActDevname, sbufs, freq, issyncn]);

      //create the sources and the buffers
      alGenSources(1, @source); //stereo feed from the game module
      //alSourcei(source,
      alGenBuffers(snd_buffers, @buffers[0]);

      alGenSources(1, @b_source); //mono backgroung sounds from the mother module

      e:= alGetError();
      if e <> AL_NO_ERROR then Die(ALErrorStr(e));

      TestGong;
    except
      Die(MI_ERROR_SOUND_INIT);
    end;
  end;


  destructor TOpenALSoundMan.Destroy;
  begin
     if Assigned(device) then begin
      //delete the buffers

      if not alcCloseDevice(device)
        then AddLog(RuEn('Не удалось закрыть устройство OpenAL! (код %0)'
                        ,'Unable to close the OpenAL device! (code %0)')
                        ,[AlcErrorStr(alcGetError(device))]);
      if b_source <> 0 then alSourceStop(b_source);
      if b_buffer <> 0 then alDeleteBuffers(1, @b_buffer);
      if buffers[0] <> 0 then alDeleteBuffers(snd_buffers, @buffers[0]);
    end;
    CloseOpenAL;
  end;


  procedure TOpenALSoundMan.SetVolume(volume: float);
  begin

  end;

  procedure TOpenALSoundMan.NewData(begins: TDateTime; samples, rate: integer; data: pointer);
  begin
    if f_muted then Enable;



  end;

  procedure TOpenALSoundMan.PlaySound(samples, rate: integer; data: pointer);
  var e: ALenum;
  begin
    try
      if b_buffer <> 0 then begin
        alSourceStop(b_source);
        alDeleteBuffers(1, @b_buffer);
        alGetError();
      end;
      alGenBuffers(1, @b_buffer);

      alBufferData(b_buffer, AL_FORMAT_MONO16, PALvoid(data), samples*2, rate);
      e:= alGetError();  if e <> AL_NO_ERROR then raise Exception.Create(ALErrorStr(e));

      alSourcei(b_source, AL_BUFFER, b_buffer);
      e:= alGetError();  if e <> AL_NO_ERROR then raise Exception.Create(ALErrorStr(e));

      alSourcePlay(b_source);
      e:= alGetError();  if e <> AL_NO_ERROR then raise Exception.Create(ALErrorStr(e));
    except
      AddLog('TOpenALSoundMan.PlaySound() failed: '#10#13'  %0', [StopDying()]);
    end;
  end;

  procedure TOpenALSoundMan.Cycle;
  begin

  end;

  procedure TOpenALSoundMan.Disable;
  begin
    if f_muted then Exit;
  end;

  procedure TOpenALSoundMan.Enable;
  begin
    if not f_muted then Exit;
  end;


