使用SDL3和SDL3_mixer实现单一音源3D定位。

下载动态库和Lazarus-SDL-3.0-Packages-and-Examples引用包

unit P3DWav;

{$mode objfpc}{$H+}
interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
  ComCtrls, SDL3, SDL3_mixer;

type
  TAppState = record
    mixer: PMIX_Mixer;
    track: PMIX_Track;
    audio: PMIX_Audio;
  end;
  PAppState = ^TAppState;
type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Image1: TImage;
    Image2: TImage;
    Label1: TLabel;
    Label2: TLabel;
    Panel1: TPanel;
    TrackBar1: TTrackBar;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Image2MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Image2MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer
      );
    procedure Image2MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure TrackBar1Change(Sender: TObject);
  private
    FDrag: Boolean;       // 是否在拖动
    FOffX, FOffY: Integer;// 鼠标偏移
    FScale: Single;       // 缩放比例
  public
    procedure Update3DPosition; // 计算 3D 坐标 end;

var
  Form1: TForm1;
  FMixer: PMIX_Mixer = nil;
  app: PAppstate = nil;
  appstate: Ppointer;
  UserStop:Boolean;
implementation

{$R *.lfm}
procedure TrackStoppedCallback(userdata: pointer; track: PMIX_Track);cdecl;
begin
  if UserStop then
    UserStop:=False
  else
    MIX_PlayTrack(track,0);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
Pos: TMIX_Point3D;
st:string;
begin
  if Button1.Caption='开始' then
  begin
    Button1.Caption:='停止';
    Pos.x:=1.0;
    Pos.y:=0.0;
    Pos.z:=2.0;
    MIX_SetTrack3DPosition(app^.track,@Pos);}
    Update3DPosition;
      UserStop:=False;
    MIX_PlayTrack(app^.track,0);
  end
  else
  begin
    Button1.Caption:='开始' ;
    UserStop:=True;
    MIX_StopTrack(app^.track,0);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  audiofname:string;
begin
  audiofname:=ExtractFilePath(ParamStr(0)) + 'sound2.wav';
  app := SDL_malloc(SizeOf(TAppstate));
  app^ := Default(TAppstate);

  SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, '5');

  if not MIX_Init then begin
    SDL_Log('Couldn''t initialize SDL_mixer: %s', SDL_GetError);
  end;

  app^.mixer := MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, nil);

  app^.audio := MIX_LoadAudio(app^.mixer, pchar(audiofname), True);

  app^.track := MIX_CreateTrack(app^.mixer);

  MIX_SetTrackAudio(app^.track, app^.audio);

  MIX_SetTrackStoppedCallback(app^.track, @TrackStoppedCallback, Nil); //' 设置结束回调
  UserStop:=True;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
    MIX_DestroyTrack(app^.track);
    MIX_DestroyAudio(app^.audio) ;
    MIX_DestroyMixer(app^.mixer) ;
  MIX_Quit;
  SDL_free(app);
end;

procedure TForm1.Image2MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
  begin
    FDrag := True;
    FOffX := X;
    FOffY := Y;
  end;
end;

procedure TForm1.Image2MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if FDrag then
  begin
    Image2.Left := Image2.Left + X - FOffX;
    Image2.Top  := Image2.Top  + Y - FOffY;
    Update3DPosition; // 实时更新坐标
  end;
end;

procedure TForm1.Image2MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  FDrag := False;
end;

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  Update3DPosition;
end;

procedure TForm1.Update3DPosition;
var
  CX1, CY1: Integer; // Image1 中心点(听者)
  CX2, CY2: Integer; // Image2 中心点(声源)
  X, Z,Y: Single;
  Pos: TMIX_Point3D;
begin
  CX1 := Image1.Left + Image1.Width div 2;
  CY1 := Image1.Top  + Image1.Height div 2;

  CX2 := Image2.Left + Image2.Width div 2;
  CY2 := Image2.Top  + Image2.Height div 2;

  X := (CX2 - CX1) / 100; 
  Z := (CY2 - CY1) / 100; 

  y:=(30-TrackBar1.Position)/10;
  Label1.Caption := Format('X=%.2f, Z=%.2f, Y=%.2f', [X, Z,y]);
  if z=0 then z:=0.01 ;{x +右-左 y+上-下 z+后-前}
    Pos.x:=x;
    Pos.y:=y;
    Pos.z:=z;
    MIX_SetTrack3DPosition(app^.track,@Pos);
end;

end.

在使用中发现左右定位没问题,但是前后没有听出变化。原因是立体音不能体现前后变化,要用4声道或5.1声道才能有用。网上查了一个物理5.1的耳机太贵了,准备自己做一个来试试效果。

 

posted on 2026-04-23 09:33  禁卫军  阅读(19)  评论(0)    收藏  举报