使用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的耳机太贵了,准备自己做一个来试试效果。
浙公网安备 33010602011771号