diff --git a/example/Loopback/loopback.lps b/example/Loopback/loopback.lps
index bec3982..957187c 100644
--- a/example/Loopback/loopback.lps
+++ b/example/Loopback/loopback.lps
@@ -21,8 +21,8 @@
-
-
+
+
@@ -312,8 +312,8 @@
-
-
+
+
@@ -428,123 +428,123 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
diff --git a/example/Loopback/unit1.pas b/example/Loopback/unit1.pas
index 8741e86..c0714bb 100644
--- a/example/Loopback/unit1.pas
+++ b/example/Loopback/unit1.pas
@@ -86,15 +86,17 @@ TForm1 = class(TForm)
FLoopbackContext: TALSLoopbackContext;
// tracks instance
FTracks: array[0..2] of TTrack;
+ FMixingTime: double;
+ FCanceled: boolean;
procedure InitTracks;
function GetSampleRate: integer;
function GetChannel: TALSLoopbackChannel;
function GetSampleType: TALSLoopbackSampleType;
- procedure EnableMix(aState: boolean);
+ procedure EnableMixGUI(aState: boolean);
private
procedure ProcessLoopbackContextOnProgress(Sender: TALSLoopbackContext;
aTimePos: double; const aFrameBuffer: TALSLoopbackFrameBuffer;
- var SaveBufferToFile: boolean; var Done: boolean);
+ var SaveBufferToFile, Cancel: boolean);
public
end;
@@ -126,9 +128,7 @@ procedure TForm1.BitBtn1Click(Sender: TObject);
procedure TForm1.BCancelClick(Sender: TObject);
begin
// User want to cancel the mix
- FLoopbackContext.CancelMix;
-
- BCancel.Tag := 1;
+ FCanceled := True;
end;
procedure TForm1.ComboBox2Select(Sender: TObject);
@@ -259,7 +259,7 @@ procedure TForm1.BMixToFileClick(Sender: TObject);
end;
- EnableMix(False); // Avoid any user's interaction.
+ EnableMixGUI(False); // Avoid any user's interaction.
// Customize our context attributes for a loopback context
FAttribs.InitDefault; // don't forget this first !
@@ -289,7 +289,7 @@ procedure TForm1.BMixToFileClick(Sender: TObject);
outputFilename := ChangeFileExt(Edit1.Text, '.wav');
outputFilename := ConcatPaths([DirectoryEdit1.Text, outputFilename]);
- // In this demo, the file output major format is wav file and the bit width is
+ // In this demo, the file output major format is 'wav' and the bit width is
// the same as the loopback context.
case ComboBox3.ItemIndex of
0: fileFormat := ALSMakeFileFormat( SF_FORMAT_WAV, SF_FORMAT_PCM_16);
@@ -303,30 +303,39 @@ procedure TForm1.BMixToFileClick(Sender: TObject);
begin
ShowMessage('Can not create output file' + LineEnding + outputFilename);
FreeAndNil(FLoopbackContext);
- EnableMix(True);
+ EnableMixGUI(True);
Exit;
end;
- // Define a callback to update our progress bar, vu-meters and controls
- // the mixing process. This callback will be fired each time a buffer is
+ // Define a callback to update our progress bar, vu-meters and controls the
+ // mixing process. This callback will be fired each time a buffer is
// filled with audio
FLoopbackContext.OnProgress := @ProcessLoopbackContextOnProgress;
- // Start the mixing -> keep the hand until the mixing is done or canceled.
- // In the meantime OnProgress callback is fired.
- FLoopbackContext.StartMixing;
+ FCanceled := False;
+
+ // We have to call this method before render audio.
+ FLoopbackContext.BeginOfMix;
+
+ repeat
+ // Ask the context to render 10Ms of audio.
+ FLoopbackContext.Mix(0.010);
+ until (FMixingTime >= FloatSpinEdit2.Value) or // mixing time reach the end of the interval
+ FCanceled; // user click cancel button
+
+ // We have to call this method at the end, to finalize the mixing process.
+ FLoopbackContext.EndOfMix;
// Checks error only if the mix was not canceled
- if BCancel.Tag = 0 then // if user clicks cancel button, its Tag is sets to 1.
+ if not FCanceled then
begin
// Check mixing error
if FLoopbackContext.MixingError then
Showmessage(FLoopbackContext.MixingStrError)
else
ShowMessage('Mixdown saved to' + lineending +
- outputFilename + lineending + 'SUCCESS');
- end
- else BCancel.Tag := 0;
+ outputFilename + lineending + 'WITH SUCCESS');
+ end;
// Free loopback context (and loopback device)
FreeAndNil(FLoopbackContext);
@@ -337,7 +346,7 @@ procedure TForm1.BMixToFileClick(Sender: TObject);
ProgressBar1.Position := 0;
ProgressBar2.Position := ALS_DECIBEL_MIN_VALUE;
ProgressBar3.Position := ALS_DECIBEL_MIN_VALUE;
- EnableMix(True);
+ EnableMixGUI(True);
end;
procedure TForm1.TrackBar1Change(Sender: TObject);
@@ -405,7 +414,7 @@ function TForm1.GetSampleType: TALSLoopbackSampleType;
end;
end;
-procedure TForm1.EnableMix(aState: boolean);
+procedure TForm1.EnableMixGUI(aState: boolean);
begin
Panel1.Enabled := aState;
Panel2.Enabled := aState;
@@ -426,8 +435,10 @@ procedure TForm1.EnableMix(aState: boolean);
//
procedure TForm1.ProcessLoopbackContextOnProgress(Sender: TALSLoopbackContext;
aTimePos: double; const aFrameBuffer: TALSLoopbackFrameBuffer;
- var SaveBufferToFile: boolean; var Done: boolean);
+ var SaveBufferToFile, Cancel: boolean);
begin
+ FMixingTime := aTimePos;
+
// update the progress bar according to the current mixing time position
ProgressBar1.Position := Round(ProgressBar1.Max*aTimePos/FloatSpinEdit2.Value);
@@ -443,8 +454,7 @@ procedure TForm1.ProcessLoopbackContextOnProgress(Sender: TALSLoopbackContext;
SaveBufferToFile := (aTimePos >= FloatSpinEdit1.Value) and
(aTimePos <= FloatSpinEdit2.Value);
- // Stops the mixing when the current mixing time position reach the end time.
- Done := aTimePos >= FloatSpinEdit2.Value;
+ Cancel := FCanceled;
end;
end.
diff --git a/example/console_alplay/console_alplay.lpr b/example/console_alplay/console_alplay.lpr
index 031ee64..4a45b1b 100644
--- a/example/console_alplay/console_alplay.lpr
+++ b/example/console_alplay/console_alplay.lpr
@@ -58,7 +58,7 @@
while OurMusic.State = ALS_PLAYING do
begin
write(#13+'Press a key to stop - Played '+
- FormatFloat('0.0', OurMusic.GetTimePosition)+'/'+
+ FormatFloat('0.0', OurMusic.TimePosition)+'/'+
FormatFloat('0.0', OurMusic.TotalDuration)+' s');
sleep(200);
if KeyPressed then OurMusic.Stop;
diff --git a/example/console_alplay/console_alplay.lps b/example/console_alplay/console_alplay.lps
index e1dd813..025553a 100644
--- a/example/console_alplay/console_alplay.lps
+++ b/example/console_alplay/console_alplay.lps
@@ -4,13 +4,12 @@
-
+
-
-
-
+
+
@@ -67,17 +66,26 @@
-
+
+
+
+
+
+
+
+
+
+
-
+
@@ -146,6 +154,14 @@
+
+
+
+
+
+
+
+
diff --git a/example/console_fileformat/console_fileformat.lps b/example/console_fileformat/console_fileformat.lps
index 49764af..fb72ef8 100644
--- a/example/console_fileformat/console_fileformat.lps
+++ b/example/console_fileformat/console_fileformat.lps
@@ -9,7 +9,7 @@
-
+
diff --git a/example/console_openal-info/console_openal_info.lps b/example/console_openal-info/console_openal_info.lps
index e7a4ae5..3171328 100644
--- a/example/console_openal-info/console_openal_info.lps
+++ b/example/console_openal-info/console_openal_info.lps
@@ -10,7 +10,7 @@
-
+
diff --git a/example/effect_parameter/EffectParameter.lpi b/example/effect_parameter/EffectParameter.lpi
index 3cc02ee..f0f65a6 100644
--- a/example/effect_parameter/EffectParameter.lpi
+++ b/example/effect_parameter/EffectParameter.lpi
@@ -102,6 +102,7 @@
+
diff --git a/example/effect_parameter/EffectParameter.lps b/example/effect_parameter/EffectParameter.lps
index ae2f8d7..36474da 100644
--- a/example/effect_parameter/EffectParameter.lps
+++ b/example/effect_parameter/EffectParameter.lps
@@ -22,7 +22,7 @@
-
+
diff --git a/example/effects/effects.lps b/example/effects/effects.lps
index 3827bea..20d97a1 100644
--- a/example/effects/effects.lps
+++ b/example/effects/effects.lps
@@ -21,8 +21,8 @@
-
-
+
+
@@ -472,123 +472,123 @@
-
+
-
-
+
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
diff --git a/example/effects/unit1.pas b/example/effects/unit1.pas
index 4e0092f..2bbebe4 100644
--- a/example/effects/unit1.pas
+++ b/example/effects/unit1.pas
@@ -492,11 +492,11 @@ procedure TForm1.Timer1Timer(Sender: TObject);
else
begin
Label1.Caption := 'time: ' +
- FormatFloat('0.00', FSound.GetTimePosition) + ' / ' +
+ FormatFloat('0.00', FSound.TimePosition) + ' / ' +
FormatFloat('0.00', FSound.TotalDuration) + 's';
ProgressBar1.Max := FSound.Seconds2Byte( FSound.TotalDuration ); // FSound.SampleCount div FSound.ChannelCount;
- ProgressBar1.Position := FSound.Seconds2Byte( FSound.GetTimePosition );
+ ProgressBar1.Position := FSound.Seconds2Byte( FSound.TimePosition );
case FSound.State of
ALS_STOPPED: s := 'STOPPED';
diff --git a/example/multi_context/MultiContext.lps b/example/multi_context/MultiContext.lps
index 29eb28a..0044993 100644
--- a/example/multi_context/MultiContext.lps
+++ b/example/multi_context/MultiContext.lps
@@ -20,9 +20,8 @@
-
-
-
+
+
@@ -106,9 +105,10 @@
+
-
-
+
+
@@ -126,123 +126,123 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
+
diff --git a/example/simplerecorder/SimpleRecorder.lps b/example/simplerecorder/SimpleRecorder.lps
index 41dc428..f519d96 100644
--- a/example/simplerecorder/SimpleRecorder.lps
+++ b/example/simplerecorder/SimpleRecorder.lps
@@ -20,7 +20,6 @@
-
@@ -33,9 +32,10 @@
+
-
-
+
+
@@ -239,124 +239,124 @@
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
diff --git a/example/simplerecorder/unit2.lfm b/example/simplerecorder/unit2.lfm
index 394947b..9a94ff2 100644
--- a/example/simplerecorder/unit2.lfm
+++ b/example/simplerecorder/unit2.lfm
@@ -11,7 +11,7 @@ object Form2: TForm2
Color = 11590399
Font.Height = 20
Position = poMainFormCenter
- LCLVersion = '2.2.3.0'
+ LCLVersion = '2.2.4.0'
object Shape1: TShape
Left = 0
Height = 187
diff --git a/example/simplerecorder/unit2.pas b/example/simplerecorder/unit2.pas
index ac7b91a..7e70685 100644
--- a/example/simplerecorder/unit2.pas
+++ b/example/simplerecorder/unit2.pas
@@ -33,7 +33,6 @@ TForm2 = class(TForm)
Form2: TForm2;
implementation
-uses unit1;
{$R *.lfm}
@@ -46,7 +45,7 @@ procedure TForm2.Timer1Timer(Sender: TObject);
// update progress bar position
if FMusic.TotalDuration <> 0 then
with FMusic do
- ProgressBar1.Position := Round( GetTimePosition / TotalDuration * ProgressBar1.Max );
+ ProgressBar1.Position := Round( TimePosition / TotalDuration * ProgressBar1.Max );
Timer1.Enabled := True;
end;
diff --git a/source/alext.inc b/source/alext.inc
index 6320a17..d3fbe52 100644
--- a/source/alext.inc
+++ b/source/alext.inc
@@ -114,11 +114,14 @@ const
ALC_CONNECTED=$313;
+type
+ TProc_alcSetThreadContext = function(context: PALCcontext): ALCboolean; cdecl;
+ TProc_alcGetThreadContext = function(): PALCcontext; cdecl;
var
//ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context);
//ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void);
- alcSetThreadContext: function(context: PALCcontext): ALCboolean; cdecl;
- alcGetThreadContext: function(): PALCcontext; cdecl;
+ _alcSetThreadContext: TProc_alcSetThreadContext; //function(context: PALCcontext): ALCboolean; cdecl;
+ _alcGetThreadContext: TProc_alcGetThreadContext; //function(): PALCcontext; cdecl;
const
AL_SOURCE_DISTANCE_MODEL=$200;
@@ -238,10 +241,14 @@ const
ALC_6POINT1_SOFT=$1505;
ALC_7POINT1_SOFT=$1506;
+type
+ TProc_alcLoopbackOpenDeviceSOFT = function(const deviceName: PALCchar): PALCdevice; cdecl;
+ TProc_alcIsRenderFormatSupportedSOFT = function(device: PALCdevice; freq: ALCsizei; channels, _type: ALCenum): ALCboolean; cdecl;
+ TProc_alcRenderSamplesSOFT = procedure(device: PALCdevice; buffer: PALCvoid; samples: ALCsizei); cdecl;
var
- alcLoopbackOpenDeviceSOFT: function(const deviceName: PALCchar): PALCdevice; cdecl;
- alcIsRenderFormatSupportedSOFT: function(device: PALCdevice; freq: ALCsizei; channels, _type: ALCenum): ALCboolean; cdecl;
- alcRenderSamplesSOFT: procedure(device: PALCdevice; buffer: PALCvoid; samples: ALCsizei); cdecl;
+ _alcLoopbackOpenDeviceSOFT: TProc_alcLoopbackOpenDeviceSOFT;
+ _alcIsRenderFormatSupportedSOFT: TProc_alcIsRenderFormatSupportedSOFT;
+ _alcRenderSamplesSOFT: TProc_alcRenderSamplesSOFT;
const
AL_STEREO_ANGLES=$1030;
@@ -293,9 +300,12 @@ const
//#define AL_SAMPLE_LENGTH_SOFT=$200A;
//#define AL_SEC_LENGTH_SOFT=$200B;
+type
+ TProc_alcDevicePauseSOFT = procedure(device: PALCdevice); cdecl;
+ TProc_alcDeviceResumeSOFT = procedure(device: PALCdevice); cdecl;
var
- alcDevicePauseSOFT: procedure(device: PALCdevice); cdecl;
- alcDeviceResumeSOFT: procedure(device: PALCdevice); cdecl;
+ _alcDevicePauseSOFT: TProc_alcDevicePauseSOFT;
+ _alcDeviceResumeSOFT: TProc_alcDeviceResumeSOFT;
const
@@ -334,9 +344,12 @@ const
ALC_HRTF_SPECIFIER_SOFT=$1995;
ALC_HRTF_ID_SOFT=$1996;
+type
+ TProc_alcGetStringiSOFT = function(device: PALCdevice; paramName: ALCenum; index: ALCsizei): PALCchar; cdecl;
+ TProc_alcResetDeviceSOFT = function(device: PALCdevice; const attribs: PALCint): ALCboolean; cdecl;
var
- alcGetStringiSOFT: function(device: PALCdevice; paramName: ALCenum; index: ALCsizei): PALCchar; cdecl;
- alcResetDeviceSOFT: function(device: PALCdevice; const attribs: PALCint): ALCboolean; cdecl;
+ _alcGetStringiSOFT: TProc_alcGetStringiSOFT;
+ _alcResetDeviceSOFT: TProc_alcResetDeviceSOFT;
const
AL_GAIN_LIMIT_SOFT=$200E;
@@ -361,6 +374,7 @@ type
ALCint64SOFT=_alsoft_int64_t;
PALCint64SOFT = ^ALCint64SOFT;
ALCuint64SOFT=_alsoft_uint64_t;
+ TProc_alcGetInteger64vSOFT = procedure(device: PALCdevice; pname: ALCenum; size: ALsizei; values: PALCint64SOFT); cdecl;
const
ALC_DEVICE_CLOCK_SOFT=$1600;
ALC_DEVICE_LATENCY_SOFT=$1601;
@@ -369,7 +383,7 @@ const
AL_SEC_OFFSET_CLOCK_SOFT=$1203;
var
- alcGetInteger64vSOFT: procedure(device: PALCdevice; pname: ALCenum; size: ALsizei; values: PALCint64SOFT); cdecl;
+ _alcGetInteger64vSOFT: TProc_alcGetInteger64vSOFT;
const
@@ -429,7 +443,7 @@ var
type
TProc_alcReopenDeviceSOFT = function(device: PALCdevice; const deviceName: PALCchar; const attribs: PALCint): ALCboolean;
var
- alcReopenDeviceSOFT: TProc_alcReopenDeviceSOFT;
+ _alcReopenDeviceSOFT: TProc_alcReopenDeviceSOFT;
// AL_SOFT_callback_buffer
diff --git a/source/als_deviceitem.inc b/source/als_deviceitem.inc
new file mode 100644
index 0000000..652c88b
--- /dev/null
+++ b/source/als_deviceitem.inc
@@ -0,0 +1,231 @@
+{$ifdef ALS_INTERFACE}
+type
+ { TALSDeviceItem }
+
+ TALSDeviceItem = object
+ private
+ alcGetInteger64vSOFT: TProc_alcGetInteger64vSOFT;
+
+ alcSetThreadContext: TProc_alcSetThreadContext;
+ alcGetThreadContext: TProc_alcGetThreadContext;
+
+ alcDevicePauseSOFT: TProc_alcDevicePauseSOFT;
+ alcDeviceResumeSOFT: TProc_alcDeviceResumeSOFT;
+
+ alcGetStringiSOFT: TProc_alcGetStringiSOFT;
+ alcResetDeviceSOFT: TProc_alcResetDeviceSOFT;
+
+ alcReopenDeviceSOFT: TProc_alcReopenDeviceSOFT;
+ procedure LoadExtension;
+ private
+ alcLoopbackOpenDeviceSOFT: TProc_alcLoopbackOpenDeviceSOFT;
+ alcIsRenderFormatSupportedSOFT: TProc_alcIsRenderFormatSupportedSOFT;
+ alcRenderSamplesSOFT: TProc_alcRenderSamplesSOFT;
+ procedure LoadLoopbackExtension;
+ procedure FirstInitdefault;
+ public
+ Name: string;
+ Handle: PALCDevice;
+ FHaveEXT_ALC_EXT_EFX,
+ FHaveExt_ALC_SOFT_output_mode,
+ FHaveExt_ALC_SOFT_HRTF,
+ FHaveExt_ALC_SOFT_loopback,
+ FHaveExt_ALC_SOFT_output_limiter,
+ FHaveExt_ALC_EXT_thread_local_context,
+ FHaveExt_ALC_SOFT_reopen_device,
+ FHaveExt_ALC_SOFT_device_clock,
+ FHaveExt_ALC_SOFT_pause_device: boolean;
+ end;
+ PALSDeviceItem = ^TALSDeviceItem;
+
+ { TALSPlaybackDeviceItem }
+
+ TALSPlaybackDeviceItem = object(TALSDeviceItem)
+ public
+ OpenedCount: integer;
+ procedure InitDefault;
+ procedure Open;
+ procedure Close;
+ procedure DoCloseDevice;
+ end;
+ PALSPlaybackDeviceItem = ^TALSPlaybackDeviceItem;
+ ArrayOfALSPlaybackDeviceItem = array of TALSPlaybackDeviceItem;
+
+ { TALSLoopbackDeviceItem }
+
+ TALSLoopbackDeviceItem = object(TALSDeviceItem)
+ OpenedCount: integer;
+ procedure InitDefault;
+ procedure Open;
+ procedure Close;
+ procedure DoCloseDevice;
+ end;
+ PALSLoopbackDeviceItem = ^TALSLoopbackDeviceItem;
+
+{$endif}
+
+{$ifdef ALS_IMPLEMENTATION}
+
+{ TALSLoopbackDeviceItem }
+
+procedure TALSLoopbackDeviceItem.InitDefault;
+begin
+ FirstInitdefault;
+ OpenedCount := 0;
+end;
+
+procedure TALSLoopbackDeviceItem.Open;
+begin
+ if not ALSManager.Error then
+ begin
+ if Handle = NIL then
+ begin
+ LoadLoopbackExtension;
+
+ if alcLoopbackOpenDeviceSOFT <> NIL then begin
+ if Name = '' then
+ Handle := alcLoopbackOpenDeviceSOFT(nil)
+ else
+ Handle := alcLoopbackOpenDeviceSOFT(PChar(Name));
+ end;
+
+ LoadExtension;
+ end;
+
+ if Handle <> NIL then
+ inc( OpenedCount );
+ end;
+end;
+
+procedure TALSLoopbackDeviceItem.Close;
+begin
+ if not ALSManager.Error then
+ begin
+ {$ifndef ALS_ENABLE_CONTEXT_SWITCHING}
+ _SingleContextIsCurrent := False;
+ {$endif}
+ if OpenedCount = 0 then
+ exit;
+ dec( OpenedCount );
+ if OpenedCount = 0 then
+ DoCloseDevice;
+ end;
+end;
+
+procedure TALSLoopbackDeviceItem.DoCloseDevice;
+begin
+ alcCloseDevice(Handle);
+ Handle := NIL;
+end;
+
+{ TALSPlaybackDeviceItem }
+
+procedure TALSPlaybackDeviceItem.InitDefault;
+begin
+ FirstInitdefault;
+ OpenedCount := 0;
+end;
+
+procedure TALSPlaybackDeviceItem.Open;
+begin
+ if not ALSManager.Error then
+ begin
+ if Handle = NIL then begin
+ Handle := alcOpenDevice(PChar(Name));
+ LoadExtension;
+ LoadLoopbackExtension;
+ end;
+ inc( OpenedCount );
+ end;
+end;
+
+procedure TALSPlaybackDeviceItem.Close;
+begin
+ if not ALSManager.Error then
+ begin
+ {$ifndef ALS_ENABLE_CONTEXT_SWITCHING}
+ _SingleContextIsCurrent := False;
+ {$endif}
+ if OpenedCount = 0 then
+ exit;
+ dec( OpenedCount );
+ if OpenedCount = 0 then
+ DoCloseDevice;
+ end;
+end;
+
+procedure TALSPlaybackDeviceItem.DoCloseDevice;
+begin
+ alcCloseDevice( Handle );
+ Handle := NIL;
+end;
+
+{ TALSDeviceItem }
+
+procedure TALSDeviceItem.LoadExtension;
+begin
+ if Handle = NIL then begin
+ alcSetThreadContext := NIL;
+ alcGetThreadContext := NIL;
+ alcDevicePauseSOFT := NIL;
+ alcDeviceResumeSOFT := NIL;
+ alcGetStringiSOFT := NIL;
+ alcResetDeviceSOFT := NIL;
+ alcGetInteger64vSOFT := NIL;
+ alcReopenDeviceSOFT := NIL;
+ end else begin
+ FHaveEXT_ALC_EXT_EFX := alcIsExtensionPresent(Handle, PChar('ALC_EXT_EFX'));
+ if FHaveEXT_ALC_EXT_EFX then
+ FHaveEXT_ALC_EXT_EFX := LoadExt_ALC_EXT_EFX;
+
+ FHaveExt_ALC_SOFT_output_mode := alcIsExtensionPresent(Handle, PChar('ALC_SOFT_output_mode'));
+
+ FHaveExt_ALC_EXT_thread_local_context := LoadExt_ALC_EXT_thread_local_context(Handle);
+ alcSetThreadContext := openalsoft._alcSetThreadContext;
+ alcGetThreadContext := openalsoft._alcGetThreadContext;
+
+ LoadLoopbackExtension;
+
+ FHaveExt_ALC_SOFT_output_limiter := alcIsExtensionPresent(Handle, PChar('ALC_SOFT_output_limiter'));
+
+ FHaveExt_ALC_SOFT_pause_device := LoadExt_ALC_SOFT_pause_device(Handle);
+ alcDevicePauseSOFT := openalsoft._alcDevicePauseSOFT;
+ alcDeviceResumeSOFT := openalsoft._alcDeviceResumeSOFT;
+
+ FHaveExt_ALC_SOFT_HRTF := LoadExt_ALC_SOFT_HRTF(Handle);
+ alcGetStringiSOFT := openalsoft._alcGetStringiSOFT;
+ alcResetDeviceSOFT := openalsoft._alcResetDeviceSOFT;
+
+ FHaveExt_ALC_SOFT_device_clock := LoadExt_ALC_SOFT_device_clock(Handle);
+ alcGetInteger64vSOFT := openalsoft._alcGetInteger64vSOFT;
+
+ FHaveExt_ALC_SOFT_reopen_device := LoadExt_ALC_SOFT_reopen_device(Handle);
+ alcReopenDeviceSOFT := openalsoft._alcReopenDeviceSOFT;
+ end;
+end;
+
+procedure TALSDeviceItem.LoadLoopbackExtension;
+begin
+ FHaveExt_ALC_SOFT_loopback := LoadExt_ALC_SOFT_loopback(NIL);
+ alcLoopbackOpenDeviceSOFT := openalsoft._alcLoopbackOpenDeviceSOFT;
+ alcIsRenderFormatSupportedSOFT := openalsoft._alcIsRenderFormatSupportedSOFT;
+ alcRenderSamplesSOFT := openalsoft._alcRenderSamplesSOFT;
+end;
+
+procedure TALSDeviceItem.FirstInitdefault;
+begin
+ Name := '';
+ Handle := NIL;
+ FHaveEXT_ALC_EXT_EFX := False;
+ FHaveExt_ALC_SOFT_output_mode := False;
+ FHaveExt_ALC_SOFT_HRTF := False;
+ FHaveExt_ALC_SOFT_loopback := False;
+ FHaveExt_ALC_SOFT_output_limiter := False;
+ FHaveExt_ALC_EXT_thread_local_context := False;
+ FHaveExt_ALC_SOFT_reopen_device := False;
+ FHaveExt_ALC_SOFT_device_clock := False;
+ FHaveExt_ALC_SOFT_pause_device := False;
+end;
+
+{$endif}
+
diff --git a/source/als_dsp_utils.pas b/source/als_dsp_utils.pas
index d3ad036..a560647 100644
--- a/source/als_dsp_utils.pas
+++ b/source/als_dsp_utils.pas
@@ -38,8 +38,10 @@ interface
ArrayOfSmallint = array of SmallInt;
ArrayOfSingle = array of single;
- // convert a percent value (range is [0..1]) to decibel
- function ALSPercentToDecibel(aValue: single): single;
+ // use LinearTodB instead
+ function ALSPercentToDecibel(aValue: single): single; deprecated;
+ function LinearTodB(aLinearValue: single): single;
+ function dBToLinear(adBValue: single): single;
// convert an array of values in range [0..1] to dB.
procedure als_dsp_ValuesToDecibel(p: PSingle; aCount: integer);
@@ -69,17 +71,29 @@ interface
procedure dsp_Amplify_Smallint(p: PSmallint; aFrameCount: longword; aChannelCount: Smallint; aGain: single);
procedure dsp_Amplify_Float(p: PSingle; aFrameCount: longword; aChannelCount: Smallint; aGain: single);
+ procedure dsp_AmplifySample_Smallint(p: PSmallint; aFrameIndex: longword; aChannelCount: Smallint; aGain: single); inline;
+ procedure dsp_AmplifySample_Float(p: PSingle; aFrameIndex: longword; aChannelCount: Smallint; aGain: single); inline;
implementation
uses Math;
function ALSPercentToDecibel(aValue: single): single;
begin
- if aValue > 0.0 then
- Result := Max(20*Log10(aValue), ALS_DECIBEL_MIN_VALUE)
+ Result := LinearTodB(aValue);
+end;
+
+function LinearTodB(aLinearValue: single): single;
+begin
+ if aLinearValue > 0.0 then
+ Result := Max(20*Log10(aLinearValue), ALS_DECIBEL_MIN_VALUE)
else
Result := ALS_DECIBEL_MIN_VALUE;
end;
+function dBToLinear(adBValue: single): single;
+begin
+ Result := Power(10, adBValue*0.05); // *0.05 same than /20
+end;
+
procedure als_dsp_ValuesToDecibel(p: PSingle; aCount: integer);
var
i: integer;
@@ -119,20 +133,23 @@ function dsp_Mean_Smallint(p: PSmallInt; aFrameCount: longword; aChannelCount: S
function dsp_Mean_Float(p: PSingle; aFrameCount: longword; aChannelCount: Smallint): ArrayOfSingle;
var
- i: longword;
+ i, fc: longword;
begin
Result := NIL;
SetLength( Result, aChannelCount );
- FillChar( Result, SizeOf(single)*aChannelCount, $00);
- while aFrameCount>0 do
+ FillChar( Result[0], SizeOf(single)*aChannelCount, $00);
+
+ fc := aFrameCount;
+ while fc>0 do
begin
for i:=0 to aChannelCount-1 do
begin
Result[i] := Result[i] + p^;
inc( p );
end;
- dec( aFrameCount );
+ dec( fc );
end;
+
for i:=0 to aChannelCount-1 do
Result[i] := Result[i] / aFrameCount;
end;
@@ -364,6 +381,30 @@ procedure dsp_Amplify_Float(p: PSingle; aFrameCount: longword;
end;
end;
+procedure dsp_AmplifySample_Smallint(p: PSmallint; aFrameIndex: longword;
+ aChannelCount: Smallint; aGain: single);
+begin
+ inc(p, aFrameIndex*aChannelCount);
+ while aChannelCount > 0 do
+ begin
+ p^ := Smallint(EnsureRange(Round(p^*aGain), -32768, 32767));
+ inc(p);
+ dec(aChannelCount);
+ end;
+end;
+
+procedure dsp_AmplifySample_Float(p: PSingle; aFrameIndex: longword;
+ aChannelCount: Smallint; aGain: single);
+begin
+ inc(p, aFrameIndex*aChannelCount);
+ while aChannelCount > 0 do
+ begin
+ p^ := p^ * aGain;
+ inc(p);
+ dec(aChannelCount);
+ end;
+end;
+
end.
diff --git a/source/als_error.inc b/source/als_error.inc
index cc0e31f..1e38615 100644
--- a/source/als_error.inc
+++ b/source/als_error.inc
@@ -126,6 +126,5 @@ begin
end;
end;
-
{$endif}
diff --git a/source/als_frame_buffers.inc b/source/als_frame_buffers.inc
index a715425..9e11243 100644
--- a/source/als_frame_buffers.inc
+++ b/source/als_frame_buffers.inc
@@ -28,13 +28,14 @@ type
function GetChannelsPeak(aIndex: integer): single;
function GetChannelsPeakdB(aIndex: integer): single;
private
- procedure FreeMemory;
function GetDataOffset(frameOffset: longword): Pointer;
procedure ResetChannelsLevelToZero;
public
+ procedure FreeMemory;
procedure ComputeChannelsLevel;
+ procedure FillWithSilence;
procedure Amplify(aGain: single);
property OutOfMemory: boolean read FOutOfMemory;
@@ -321,6 +322,12 @@ begin
FChannelsPeak[i] := FChannelsLevel[i];
end;
+procedure TALSFrameBufferBase.FillWithSilence;
+begin
+ if FUseFloat then
+ FillChar(FData^, FFrameCapacity*FBytePerFrame, $00);
+end;
+
procedure TALSFrameBufferBase.Amplify(aGain: single);
begin
if aGain = 1.0 then
@@ -343,7 +350,7 @@ end;
function TALSFrameBufferBase.GetChannelsLeveldB(aIndex: integer): single;
begin
if (aIndex >= 0) and (aIndex < Length(FChannelsLevel)) then
- Result := ALSPercentToDecibel(FChannelsLevel[aIndex])
+ Result := LinearTodB(FChannelsLevel[aIndex])
else
Result := ALS_DECIBEL_MIN_VALUE;
end;
@@ -359,7 +366,7 @@ end;
function TALSFrameBufferBase.GetChannelsPeakdB(aIndex: integer): single;
begin
if (aIndex >= 0) and (aIndex < Length(FChannelsPeak)) then
- Result := ALSPercentToDecibel(FChannelsPeak[aIndex])
+ Result := LinearTodB(FChannelsPeak[aIndex])
else
Result := ALS_DECIBEL_MIN_VALUE;
end;
diff --git a/source/als_velocity_curve.inc b/source/als_velocity_curve.inc
index fd44108..2985506 100644
--- a/source/als_velocity_curve.inc
+++ b/source/als_velocity_curve.inc
@@ -73,7 +73,6 @@ type
FValue, FConstPerSecond: single;
FState: TParamState;
FCurve: TVelocityCurve;
- procedure OnElapse(const AElapsedSec: single); virtual;
private
FOnLockParam: TALSLockParamProc;
FOnUnlockParam: TALSUnlockParamProc;
@@ -86,6 +85,7 @@ type
constructor Create;
destructor Destroy; override;
+ procedure OnElapse(const AElapsedSec: single); virtual;
// use velocity curve
procedure ChangeTo(aNewValue, aSecond: single; aCurveID: TALSCurveID = ALS_Linear); virtual;
// add a constant per second
@@ -105,7 +105,6 @@ type
FMaxValue: single;
FLoop: boolean;
procedure ApplyBounds(var AValue: single);
- procedure OnElapse(const AElapsedSec: single); override;
protected
function GetValue: single; override;
procedure SetValue(AValue: single); override;
@@ -113,6 +112,7 @@ type
// if Loop is set to TRUE, value can loop between bounds (usefull for i.e. [0..360] angle)
// if it's set to FALSE, value is clamped to FMinValue and FMaxValue.
constructor Create(aMin, aMax, aStartValue: single; aLooped: boolean=False);
+ procedure OnElapse(const AElapsedSec: single); override;
end;
diff --git a/source/alsound.pas b/source/alsound.pas
index 1e8ea11..ab65b23 100644
--- a/source/alsound.pas
+++ b/source/alsound.pas
@@ -35,7 +35,7 @@ interface
als_dsp_utils;
const
- ALS_VERSION = '1.3.0';
+ ALS_VERSION = '2.0.0';
type
@@ -305,6 +305,7 @@ TALSEffect = record
{$include als_directfilter.inc}
{$include als_frame_buffers.inc}
{$include als_velocity_curve.inc}
+ {$include als_deviceitem.inc}
type
{ TALSErrorHandling }
@@ -330,8 +331,10 @@ TALSErrorHandling = class
end;
+ TALSNotifyEvent = procedure(Sender: TALSSound) of object;
TALSOnCustomDSP = procedure(Sender: TALSSound;
- const aBuffer: TALSPlaybackBuffer) of object;
+ const aBuffer: TALSPlaybackBuffer;
+ aUserData: Pointer) of object;
{ TALSSound }
@@ -402,7 +405,11 @@ TALSSound = class( TALSErrorHandling )
procedure FreeParameters;
private
FOnCustomDSP: TALSOnCustomDSP;
- procedure SetOnCustomDSP(AValue: TALSOnCustomDSP);
+ FOnCustomDSPUserData: pointer;
+ private
+ FPreviousState: TALSState;
+ FOnStopped: TALSNotifyEvent;
+ procedure SetOnStopped(AValue: TALSNotifyEvent);
private
function GetTimePosition: single; virtual;
procedure SetTimePosition(AValue: single); virtual;
@@ -477,6 +484,15 @@ TALSSound = class( TALSErrorHandling )
// Sets the position of the sound in the 3D world
procedure Position3D( aX, aY, aZ: single );
+ public
+ // Use this method to define a callback to apply your custom DSP effects.
+ // This callback is called when a buffer is filled with new raw audio data
+ // and before send it to OpenAL-Soft pipeline.
+ // Only once for a static sound, when data are loaded or generated in memory.
+ // Each time a new buffer is read from a streamed sound.
+ // Your callback must be fast and mustn't update any GUI items.
+ procedure SetOnCustomDSP(aProc: TALSOnCustomDSP; aUserData: Pointer);
+
public
// The volume of the sound. Range is [0.0 to 8.0]
// 0.0=silence 1.0=original volume >1.0=amplification
@@ -534,13 +550,7 @@ TALSSound = class( TALSErrorHandling )
// For more info see https://openal-soft.org/openal-extensions/SOFT_source_resampler.txt
property ResamplerIndex: integer read GetResamplerIndex write SetResamplerIndex;
- // Use this callback to apply your custom DSP effects on the given buffer.
- // This callback is called when a buffer is filled with new raw audio data
- // and before send it to OpenAL-Soft.
- // Only once for a static sound, when data are loaded or generated in memory.
- // Each time a new buffer is read from a streamed sound.
- // Your callback must be fast and mustn't update any GUI items.
- property OnNewBuffer: TALSOnCustomDSP read FOnCustomDSP write SetOnCustomDSP;
+ property OnStopped: TALSNotifyEvent read FOnStopped write SetOnStopped;
// State of the sound. Possible value are ALS_STOPPED, ALS_PLAYING, ALS_PAUSED
property State: TALSState read GetState;
@@ -565,12 +575,14 @@ TALSSingleStaticBufferSound = class(TALSSound)
constructor CreateFromFile(aParent: TALSPlaybackContext;
const aFilename: string;
aEnableMonitor: boolean;
- aOnCustomDSP: TALSOnCustomDSP);
+ aOnCustomDSP: TALSOnCustomDSP;
+ aCustomDSPUserData: Pointer);
constructor CreateWhiteNoise(aParent: TALSPlaybackContext;
aDuration: single;
aChannelCount: integer;
aEnableMonitor: boolean;
- aOnCustomDSP: TALSOnCustomDSP);
+ aOnCustomDSP: TALSOnCustomDSP;
+ aCustomDSPUserData: Pointer);
destructor Destroy; override;
end;
@@ -607,11 +619,13 @@ TALSStreamBufferSound = class(TALSSound)
constructor CreateFromFile(aParent: TALSPlaybackContext;
const aFilename: string;
aEnableMonitor: boolean;
- aOnCustomDSP: TALSOnCustomDSP);
+ aOnCustomDSP: TALSOnCustomDSP;
+ aCustomDSPUserData: Pointer);
{constructor CreateFromUrl(aParent: TALSPlaybackContext;
const aUrl: string;
aEnableMonitor: boolean;
- aOnCustomDSP: TALSOnCustomDSP); }
+ aOnCustomDSP: TALSOnCustomDSP;
+ aCustomDSPUserData: Pointer); }
destructor Destroy; override;
end;
@@ -760,14 +774,18 @@ TALSPlaybackContext = class(TALSErrorHandling)
FCriticalSection: TRTLCriticalSection;
FThread: TALSThread;
FThreadIsStarted: boolean;
+ FSoundToProcess: TALSSound;
procedure DoUpdate(const aElapsedTime: single);
+ procedure DoSoundOnStopped;
procedure EnterCS;
procedure LeaveCS;
protected
FExecutingConstructor: boolean;
private
+ FHaveExt_ALC_SOFT_HRTF: boolean;
FList: TFPList;
FPlaylist: TALSPlaylist;
+ FParentDeviceItem: PALSDeviceItem;
FParentDevice: PALCdevice;
FContext: PALCcontext;
FDistanceModel: TALSDistanceModel;
@@ -777,19 +795,14 @@ TALSPlaybackContext = class(TALSErrorHandling)
FAuxiliarySendAvailable: ALCInt;
FDefaultResamplerIndex: integer;
- FHaveEXT_ALC_EXT_EFX,
FHaveLowPassFilter,
FHaveBandPassFilter,
FHaveHighPassFilter,
FHaveExt_AL_SOFT_effect_target,
FHaveExt_AL_EXT_STEREO_ANGLES,
FHaveExt_AL_EXT_BFORMAT,
- FHaveExt_ALC_SOFT_HRTF,
- FHaveExt_ALC_SOFT_output_mode,
FHaveExt_AL_SOFT_deferred_updates,
FHaveExt_AL_SOFT_source_resampler,
- FHaveExt_ALC_SOFT_loopback,
- FHaveExt_ALC_SOFT_output_limiter,
FHaveExt_AL_SOFT_source_spatialize,
FHaveExt_AL_SOFT_gain_clamp_ex,
FHaveExt_AL_EXT_source_distance_model,
@@ -800,6 +813,8 @@ TALSPlaybackContext = class(TALSErrorHandling)
FInternalSampleType: TALSPlaybackSampleType;
+ function GetHaveEXT_ALC_EXT_EFX: boolean;
+ function GetHaveExt_ALC_SOFT_HRTF: boolean;
function GetHaveFilter: boolean;
function GetHRTFEnabled: boolean;
function GetHRTFList: TStringArray;
@@ -821,7 +836,7 @@ TALSPlaybackContext = class(TALSErrorHandling)
// Don't create playback context directly.
// Use ALSManager.CreateDefaultPlaybackContext
// or ALSManager.CreatePlaybackContext(...) method for this
- constructor Create(aDevice: PALCDevice; const aAttribs: TALSContextAttributes);
+ constructor Create(aDevice: PALSPlaybackDeviceItem; const aAttribs: TALSContextAttributes);
destructor Destroy; override;
// Loads a sound file into memory and return its instance.
@@ -829,13 +844,15 @@ TALSPlaybackContext = class(TALSErrorHandling)
// sound (it take some ram and cpu resources).
function AddSound(const aFilename: string;
aEnableMonitoring: boolean=False;
- aOnCustomDSP: TALSOnCustomDSP=NIL): TALSSound;
+ aOnCustomDSP: TALSOnCustomDSP=NIL;
+ aCustomDSPUserData: Pointer=NIL): TALSSound;
// Opens the sound file as stream and return its instance.
// Set aEnableMonitoring to True if you need the channel's level of the
// sound (it take some ram and cpu resources).
function AddStream(const aFilename: string;
aEnableMonitoring: boolean=False;
- aOnCustomDSP: TALSOnCustomDSP=NIL): TALSSound;
+ aOnCustomDSP: TALSOnCustomDSP=NIL;
+ aCustomDSPUserData: Pointer=NIL): TALSSound;
{ TODO : AddWebStream( const aUrl: string ): TOALSound; to play audio from url }
@@ -845,7 +862,8 @@ TALSPlaybackContext = class(TALSErrorHandling)
function CreateWhiteNoise(aDuration: single;
aChannelCount: integer;
aEnableMonitoring: boolean=False;
- aOnCustomDSP: TALSOnCustomDSP=NIL): TALSSound;
+ aOnCustomDSP: TALSOnCustomDSP=NIL;
+ aCustomDSPUserData: Pointer=NIL): TALSSound;
// stops the sound and free it
procedure Delete(ASound: TALSSound);
@@ -907,7 +925,7 @@ TALSPlaybackContext = class(TALSErrorHandling)
property ObtainedAuxiliarySendCount: integer read GetObtainedAuxiliarySendCount;
property HaveStereoAngle: boolean read FHaveExt_AL_EXT_STEREO_ANGLES;
- property HaveEFX: boolean read FHaveEXT_ALC_EXT_EFX;
+ property HaveEFX: boolean read GetHaveEXT_ALC_EXT_EFX;
property HaveFilter: boolean read GetHaveFilter;
// The list of available resampler.
@@ -917,7 +935,7 @@ TALSPlaybackContext = class(TALSErrorHandling)
property HaveExt_AL_SOFT_source_resampler: boolean read FHaveExt_AL_SOFT_source_resampler;
// True if the context have the HRTF capability.
- property HaveHRTF: boolean read FHaveExt_ALC_SOFT_HRTF;
+ property HaveHRTF: boolean read GetHaveExt_ALC_SOFT_HRTF;
// Gives the list of available HRTF.
property HRTFList: TStringArray read GetHRTFList;
// Return the success (True) or failure (False) of a HRTF change after a
@@ -936,7 +954,7 @@ TALSLoopbackContext = class;
aTimePos: double;
const aFrameBuffer: TALSLoopbackFrameBuffer;
var SaveBufferToFile: boolean;
- var Done: boolean) of object;
+ var Cancel: boolean) of object;
{ TALSLoopbackContext }
@@ -949,10 +967,10 @@ TALSLoopbackContext = class(TALSPlaybackContext)
function GetMixingStrError: string;
private
FOnProgress: TALSProgressEvent;
- FTimeSlice: double;
+ FTimeSlice,
+ FMixTime: double;
FFrameBuffer: TALSLoopbackFrameBuffer;
- FIsMixing,
- FCancelMixing: boolean;
+ FIsMixing: boolean;
procedure SetTimeSlice(AValue: double);
procedure Update(const aElapsedTime: double);
procedure InternalCloseDevice; override;
@@ -962,10 +980,13 @@ TALSLoopbackContext = class(TALSPlaybackContext)
FFile: PSNDFILE;
private
procedure RenderAudioToBuffer;
+ procedure SaveBufferToFile;
+ procedure CloseFile;
+ procedure DoExceptionNoCallback;
public
// Don't call directly this constructor. Instead, use method
// ALSManager.CreateDefaultLoopbackContext.
- constructor Create(aDevice: PALCDevice);
+ constructor Create(aDevice: PALSLoopbackDeviceItem);
// Checks if the specified attributes are supported by a loopback context.
function IsAttributesSupported( aSampleRate: integer;
@@ -982,20 +1003,23 @@ TALSLoopbackContext = class(TALSPlaybackContext)
// ALSManager.ListOfSimpleAudioFileFormat[].Format
function PrepareSavingToFile(const aFilename: string; aFileFormat: TALSFileFormat): boolean;
- // Start the mixing and keeps the hand until the mixing is done or canceled.
- // Callback OnProgress is fired each time the buffer is filled with audio,
- // (by default every 10ms) and, for LCL based application,
- // Application.ProcessMessage is called regularly.
- procedure StartMixing;
+ // Call this method before start your mixing process, before any calls to
+ // Mix(...)
+ procedure BeginOfMix;
- // Cancel the current mixing process. If the mix is saved to an output file,
- // the file is deleted.
- // This method should be used e.g when user click cancel button to
- // interrupt a mixing process.
- procedure CancelMix;
+ // Call this method to render audio. "OnProgress" event is triggered every
+ // time a buffer is filled.
+ // The buffer length can be adjusted with property "TimeSlice"
+ // Don't forget to put some sounds in playing state otherwise the result
+ // will be nothing but silence !
+ procedure Mix(aDuration: single);
- // This callback is fired after a call to StartMixing, each time the buffer
- // is filled with audio. It allows your application to:
+ // Call this method at the end of your mixing process. It close the output
+ // file (if any), free buffer memory, etc...
+ procedure EndOfMix;
+
+ // This callback is triggered each time the buffer is filled with audio.
+ // It allows your application to:
// - control if the current audio buffer content must be saved to the
// output file. Usefull to save only a portion of the mix.
// - control if the mixing must be stopped.
@@ -1129,32 +1153,6 @@ TALSCaptureContext = class(TALSErrorHandling)
property StrCaptureError: string read GetStrCaptureError;
end;
- { TALSPlaybackDeviceItem }
-
- TALSPlaybackDeviceItem = record
- Name: string;
- Handle: PALCDevice;
- OpenedCount: integer;
- procedure InitDefault;
- procedure Open;
- procedure Close;
- procedure DoCloseDevice;
- end;
- ArrayOfALSPlaybackDeviceItem = array of TALSPlaybackDeviceItem;
-
- { TALSLoopbackDeviceItem }
-
- TALSLoopbackDeviceItem = record
- Name: string;
- Handle: PALCDevice;
- OpenedCount: integer;
- procedure InitDefault;
- procedure Open;
- procedure Close;
- procedure DoCloseDevice;
- end;
-
-
TALSAudioFileSubFormat = record
Name: string;
Format: longint; //cint;
@@ -1246,7 +1244,7 @@ TALSManager = class( TALSErrorHandling )
function CreateDefaultPlaybackContext: TALSPlaybackContext;
// Opens the specified playback device and creates a context on it with
- // custom context attributes.
+ // custom attributes.
// aNameIndex is an index in the list provided by ListOfPlaybackDeviceName.
// You can set aNameIndex to -1 to refer to the default playback device.
function CreatePlaybackContext(aNameIndex: integer;
@@ -1354,7 +1352,6 @@ procedure UnlockContext;
{$endif}
end;
-
{$undef ALS_INTERFACE}
{$define ALS_IMPLEMENTATION}
{$include als_error.inc}
@@ -1363,6 +1360,7 @@ procedure UnlockContext;
{$include als_directfilter.inc}
{$include als_frame_buffers.inc}
{$include als_velocity_curve.inc}
+{$include als_deviceitem.inc}
{$undef ALS_IMPLEMENTATION}
function ALSMakeFileFormat(aFileMajorFormat: TALSFileMajorFormat;
@@ -1428,13 +1426,48 @@ procedure TALSLoopbackContext.RenderAudioToBuffer;
begin
LockContext(FContext);
try
- alcRenderSamplesSOFT(FParentDevice, FFrameBuffer.Data, ALCsizei(FFrameBuffer.FrameCapacity));
+ FParentDeviceItem^.alcRenderSamplesSOFT(FParentDevice,
+ FFrameBuffer.Data, ALCsizei(FFrameBuffer.FrameCapacity));
FFrameBuffer.FrameCount := FFrameBuffer.FrameCapacity;
finally
UnlockContext;
end;
end;
+procedure TALSLoopbackContext.SaveBufferToFile;
+var written: sf_count_t;
+begin
+ if (FFile <> NIL) then
+ begin
+ case FFrameBuffer.SampleType of
+ ALC_SHORT_SOFT: written := sf_writef_short(FFile, FFrameBuffer.Data, FFrameBuffer.FrameCount);
+ ALC_INT_SOFT: written := sf_writef_int(FFile, FFrameBuffer.Data, FFrameBuffer.FrameCount);
+ ALC_FLOAT_SOFT: written := sf_writef_float(FFile, FFrameBuffer.Data, FFrameBuffer.FrameCount);
+ end;
+
+ // check write file error
+ if written <> sf_count_t(FFrameBuffer.FrameCount) then
+ SetMixingError(als_FileWriteErrorWhileMixing);
+ end;
+end;
+
+procedure TALSLoopbackContext.CloseFile;
+begin
+ if FFile <> NIL then
+ begin
+ // close file
+ sf_write_sync(FFile);
+ sf_close(FFile);
+
+ FFile := NIL;
+ end;
+end;
+
+procedure TALSLoopbackContext.DoExceptionNoCallback;
+begin
+ Raise Exception.Create('TALSLoopbackContext.StartMixing - Callback OnProgress must be defined');
+end;
+
procedure TALSLoopbackContext.SetTimeSlice(AValue: double);
begin
if FIsMixing then exit;
@@ -1454,16 +1487,17 @@ function TALSLoopbackContext.GetMixingStrError: string;
Result := ALSound.GetStrError(FLoopbackError);
end;
-constructor TALSLoopbackContext.Create(aDevice: PALCDevice);
+constructor TALSLoopbackContext.Create(aDevice: PALSLoopbackDeviceItem);
begin
FExecutingConstructor := True;
InitializeErrorStatus;
ResetMixingError;
- FParentDevice := aDevice;
+ FParentDevice := aDevice^.Handle;
+ FParentDeviceItem := PALSDeviceItem(aDevice);
- if aDevice = NIL then
+ if aDevice^.Handle = NIL then
SetError(als_ALCanNotOpenLoopbackDevice)
- else if not alcIsExtensionPresent(aDevice, PChar('ALC_SOFT_loopback')) then
+ else if not aDevice^.FHaveExt_ALC_SOFT_loopback then
SetError(als_ALContextCanNotLoopback);
InitCriticalSection( FCriticalSection );
@@ -1481,7 +1515,7 @@ function TALSLoopbackContext.IsAttributesSupported(aSampleRate: integer;
Result := False
else
begin
- Result := alcIsRenderFormatSupportedSOFT(FParentDevice,
+ Result := FParentDeviceItem^.alcIsRenderFormatSupportedSOFT(FParentDevice,
ALCsizei(aSampleRate), ALCsizei(Ord(aChannels)), ALCenum(Ord(aSampleType)));
alcGetError(FPArentDevice);
end;
@@ -1523,183 +1557,69 @@ function TALSLoopbackContext.PrepareSavingToFile(const aFilename: string;
FFilename := aFilename;
// Create output audio file
FFileInfo.SampleRate := FSampleRate;
- FFileInfo.Format := aFileFormat;
+ FFileInfo. Format := aFileFormat;
FFileInfo.Channels := FFrameBuffer.ChannelCount;
- FFile := sf_open(PChar(aFilename), SFM_WRITE, @FFileInfo);
-{ if FFile = NIL then
- SetMixingError(als_CanNotCreateTargetMixFile); }
+ FFile := ALSOpenAudioFile(aFilename, SFM_WRITE, @FFileInfo);
Result := FFile <> NIL;
end;
-procedure TALSLoopbackContext.StartMixing;
-var
- posTime: double;
- written: sf_count_t;
- done, flagSaveToFile: boolean;
+procedure TALSLoopbackContext.BeginOfMix;
begin
- if Error or
- FIsMixing then
- exit;
-
if FOnProgress = NIL then
- Raise Exception.Create('TALSLoopbackContext.StartMixing - Callback OnProgress must be defined');
-
- ResetMixingError;
-
- FIsMixing := True;
- FCancelMixing := False;
-
- // reserves buffer memory
- FFrameBuffer.FrameCapacity := Round(FSampleRate*FTimeSlice);
-
- posTime := 0.0;
- done := False;
- flagSaveToFile := True;
-
- while not MixingError and
- not FCancelMixing and not done do
- begin
- // Render some audio
- RenderAudioToBuffer;
-
- // Call the callback
- FOnProgress(Self, posTime+FTimeSlice, FFrameBuffer, flagSaveToFile, done);
-
- if (FFile <> NIL) and flagSaveToFile then
- begin// save audio to file
- case FFrameBuffer.SampleType of
- ALC_SHORT_SOFT: written := sf_writef_short(FFile, FFrameBuffer.Data, FFrameBuffer.FrameCount);
- ALC_INT_SOFT: written := sf_writef_int(FFile, FFrameBuffer.Data, FFrameBuffer.FrameCount);
- ALC_FLOAT_SOFT: written := sf_writef_float(FFile, FFrameBuffer.Data, FFrameBuffer.FrameCount);
- end;
-
- // check write file error
- if written <> sf_count_t(FFrameBuffer.FrameCount) then
- SetMixingError(als_FileWriteErrorWhileMixing);
- end;
-
- {$ifdef LCL}
- Application.ProcessMessages;
- {$endif}
-
- Update(FTimeSlice);
-
- posTime := posTime + FTimeSlice;
- end;
-
- if FFile <> NIL then
- begin
- // close file
- sf_write_sync(FFile);
- sf_close(FFile);
-
- // operation was canceled ?
- if FCancelMixing then
- DeleteFile(FFilename);
-
- FFile := NIL;
+ DoExceptionNoCallback
+ else begin
+ FIsMixing := True;
+ FMixTime := 0.0;
end;
-
- FFrameBuffer.FreeMemory;
- FIsMixing := False;
end;
-procedure TALSLoopbackContext.CancelMix;
+procedure TALSLoopbackContext.Mix(aDuration: single);
+var
+ deltaTime: double;
+ flagSaveToFile, flagCancel: boolean;
begin
- FCancelMixing := True;
-end;
+ if Error or MixingError then
+ exit;
-{ TALSLoopbackDeviceItem }
+ flagSaveToFile := (FFile <> NIL);
+ flagCancel := False;
-procedure TALSLoopbackDeviceItem.InitDefault;
-begin
- Name := '';
- Handle := nil;
- OpenedCount := 0;
-end;
-
-procedure TALSLoopbackDeviceItem.Open;
-begin
- if not ALSManager.Error then
+ while (aDuration > 0.0005) and not MixingError and not flagCancel do
begin
- if Handle = NIL then
- begin
- if alcIsExtensionPresent(NIL, PChar('ALC_SOFT_loopback')) then
- begin
- if LoadExt_ALC_SOFT_loopback(NIL) then
- if Name = '' then
- Handle := alcLoopbackOpenDeviceSOFT(nil)
- else
- Handle := alcLoopbackOpenDeviceSOFT(PChar(Name));
- end;
- end;
- inc( OpenedCount );
- end;
-end;
+ // we need to mix slice of time defined by user
+ deltaTime := Min(aDuration, FTimeSlice);
+ aDuration := aDuration - deltaTime;
-procedure TALSLoopbackDeviceItem.Close;
-begin
- if not ALSManager.Error then
- begin
- {$ifndef ALS_ENABLE_CONTEXT_SWITCHING}
- _SingleContextIsCurrent := False;
- {$endif}
- if OpenedCount = 0 then
- exit;
- dec( OpenedCount );
- if OpenedCount = 0 then
- DoCloseDevice;
- end;
-end;
+ // reserves buffer memory
+ if FFrameBuffer.FrameCapacity <> Round(FSampleRate*deltaTime) then
+ FFrameBuffer.FrameCapacity := Round(FSampleRate*deltaTime);
-procedure TALSLoopbackDeviceItem.DoCloseDevice;
-begin
- alcCloseDevice(Handle);
- Handle := NIL;
-end;
+ // Render audio
+ RenderAudioToBuffer;
-{ TALSPlaybackDeviceItem }
+ // update the context
+ deltaTime := FFrameBuffer.FrameCount/FSampleRate;
+ Update(deltaTime);
+ FMixTime := FMixTime + deltaTime;
-procedure TALSPlaybackDeviceItem.InitDefault;
-begin
- Name := '';
- Handle := nil;
- OpenedCount := 0;
-end;
+ // Callback
+ FOnProgress(Self, FMixTime, FFrameBuffer, flagSaveToFile, flagCancel);
-procedure TALSPlaybackDeviceItem.Open;
-begin
- if not ALSManager.Error then
- begin
- if Handle = NIL then
- Handle := alcOpenDevice(PChar(Name));
- inc( OpenedCount );
+ if flagSaveToFile then
+ SaveBufferToFile;
end;
end;
-procedure TALSPlaybackDeviceItem.Close;
+procedure TALSLoopbackContext.EndOfMix;
begin
- if not ALSManager.Error then
- begin
- {$ifndef ALS_ENABLE_CONTEXT_SWITCHING}
- _SingleContextIsCurrent := False;
- {$endif}
- if OpenedCount = 0 then
- exit;
- dec( OpenedCount );
- if OpenedCount = 0 then
- DoCloseDevice;
+ if FIsMixing then begin
+ CloseFile;
+ FFrameBuffer.FreeMemory;
+ FIsMixing := False;
end;
end;
-procedure TALSPlaybackDeviceItem.DoCloseDevice;
-begin
- alcCloseDevice( Handle );
- Handle := NIL;
-end;
-
-
{ TALSAudioFileFormat }
function TALSAudioFileFormat.SubFormatCount: integer;
@@ -1920,7 +1840,7 @@ function TALSCaptureContext.GetCaptureError: boolean;
function TALSCaptureContext.GetChannelsLeveldB(Index: integer): single;
begin
- Result := ALSPercentToDecibel(GetChannelsLevel(Index));
+ Result := LinearTodB(GetChannelsLevel(Index));
end;
function TALSCaptureContext.GetChannelsPeak(Index: integer): single;
@@ -1935,7 +1855,7 @@ function TALSCaptureContext.GetChannelsPeak(Index: integer): single;
function TALSCaptureContext.GetChannelsPeakdB(Index: integer): single;
begin
- Result := ALSPercentToDecibel(GetChannelsPeak(Index));
+ Result := LinearTodB(GetChannelsPeak(Index));
end;
procedure TALSCaptureContext.SetMonitoringEnabled(AValue: boolean);
@@ -2064,7 +1984,7 @@ function TALSCaptureContext.PrepareSavingToFile(const aFileName: string; aFormat
tempInfo.SampleRate := FSampleRate;
tempInfo.Format := Ord(SF_ENDIAN_CPU) or Ord(SF_FORMAT_AU) or FTempFileSubFormat;
tempInfo.Channels := FCapturedFrames.ChannelCount;
- FTempFile := sf_open(PChar(FTempFileName), SFM_WRITE, @tempInfo);
+ FTempFile := ALSOpenAudioFile(FTempFileName, SFM_WRITE, @tempInfo);
if FTempFile = nil then
FCaptureError := als_CanNotOpenTemporaryCaptureFile;
@@ -2166,14 +2086,14 @@ procedure TALSCaptureContext.StopCapture;
begin
// reopen the temporary '.au' file for reading
fileInfo.Format := 0;
- FTempFile := sf_open(PChar(FTempFileName), SFM_READ, @fileInfo);
+ FTempFile := ALSOpenAudioFile(FTempFileName, SFM_READ, @fileInfo);
if FTempFile = nil then
SetCaptureError(als_CanNotOpenTemporaryCaptureFile);
if not CaptureError then
begin
// open the 'final' file with the requested format
- finalFile := sf_open(PChar(FUserFileName), SFM_WRITE, @FUserFileInfo);
+ finalFile := ALSOpenAudioFile(FUserFileName, SFM_WRITE, @FUserFileInfo);
if finalFile = nil then
begin
SetCaptureError(als_CanNotCreateTargetCaptureFile);
@@ -2377,10 +2297,13 @@ procedure TALSManager.RetrievePlaybackDevices;
begin
FPlaybackDevices := NIL;
FDefaultPlaybackDeviceIndex := -1;
+
if not Error then
begin
- _defaultDeviceName := openalsoft.GetDefaultDeviceName;
A := openalsoft.GetDeviceNames;
+ if Length(A) = 0 then exit;
+
+ _defaultDeviceName := openalsoft.GetDefaultDeviceName;
SetLength(FPlaybackDevices, Length(A));
for i:=0 to High(A) do
begin
@@ -2659,7 +2582,7 @@ function TALSManager.MixModeIndexToEnum(aIndex: integer): TALSPlaybackContextOut
function TALSManager.CreateDefaultLoopbackContext: TALSLoopbackContext;
begin
FDefaultLoopbackDevice.Open;
- Result := TALSLoopbackContext.Create(FDefaultLoopbackDevice.Handle);
+ Result := TALSLoopbackContext.Create(@FDefaultLoopbackDevice);
end;
function TALSManager.CreateDefaultPlaybackContext: TALSPlaybackContext;
@@ -2676,18 +2599,14 @@ function TALSManager.CreatePlaybackContext(aNameIndex: integer;
if aNameIndex = -1 then
aNameIndex := FDefaultPlaybackDeviceIndex;
- if not Error then
- begin
- if (aNameIndex < 0) or (aNameIndex > High(ListOfPlaybackDeviceName)) then
- Result := TALSPlaybackContext.Create(NIL, aAttribs)
- else
- begin
- FPlaybackDevices[aNameIndex].Open;
- Result := TALSPlaybackContext.Create(FPlaybackDevices[aNameIndex].Handle, aAttribs);
- end;
- end
- else
- Result := TALSPlaybackContext.Create(NIL, aAttribs);
+ if Error or
+ (aNameIndex < 0) or
+ (aNameIndex >= Length(ListOfPlaybackDeviceName)) then
+ Result := TALSPlaybackContext.Create(NIL, aAttribs)
+ else begin
+ FPlaybackDevices[aNameIndex].Open;
+ Result := TALSPlaybackContext.Create(@FPlaybackDevices[aNameIndex], aAttribs);
+ end;
end;
function TALSManager.CreateDefaultCaptureContext: TALSCaptureContext;
@@ -2744,6 +2663,7 @@ procedure TALSContextAttributes.SetLoopbackMode(aSampleRate: integer;
SampleRate := ALint(aSampleRate);
FLoopbackChannelType := aChannels;
FLoopbackSampleType := aSampleType;
+ ContextUseFloat := aSampleType = ALC_FLOAT_SOFT;
end;
function TALSContextAttributes.ToArray( aEFXPresent, aOutputModePresent,
@@ -2918,7 +2838,7 @@ procedure TALSPlaylist.LoadCurrentMusic;
begin
FreeCurrentMusic;
try
- FMusic := TALSStreamBufferSound.CreateFromFile(FParentContext, FList.Strings[FMusicIndex], False, NIL);
+ FMusic := TALSStreamBufferSound.CreateFromFile(FParentContext, FList.Strings[FMusicIndex], False, NIL, NIL);
except
FMusic.Free;
FMusic := nil;
@@ -3439,7 +3359,7 @@ procedure TALSEffect.SetMute(AValue: boolean);
not FReady then
exit;
if FParentContext.Error or
- not FParentContext.FHaveEXT_ALC_EXT_EFX then
+ not FParentContext.FParentDeviceItem^.FHaveEXT_ALC_EXT_EFX then
exit;
LockContext( FParentContext.FContext );
@@ -3606,7 +3526,7 @@ procedure TALSEffect.SetApplyDistanceAttenuation(AValue: boolean);
FApplyDistanceAttenuation:=AValue;
if FParentContext.Error or
- not FParentContext.FHaveEXT_ALC_EXT_EFX or
+ not FParentContext.FParentDeviceItem^.FHaveEXT_ALC_EXT_EFX or
not Ready then
Exit;
@@ -3747,6 +3667,7 @@ procedure TALSPlaybackCapturedSound.SetFileName(const aName: string);
procedure TALSPlaybackCapturedSound.SetTimePosition(AValue: single);
begin
// Do nothing here
+ AValue := AValue; // avoid hint
end;
constructor TALSPlaybackCapturedSound.CreateFromCapture(aParent: TALSPlaybackContext;
@@ -3861,7 +3782,7 @@ procedure TALSStreamBufferSound.PreBuffAudio;
break;
if FOnCustomDSP <> NIL then
- FOnCustomDSP(Self, FBuffers[i]);
+ FOnCustomDSP(Self, FBuffers[i], FOnCustomDSPUserData);
// refill AL buffer with audio
alBufferData(FBuffers[i].BufferID, FFormatForAL, FBuffers[i].Data,
@@ -3894,63 +3815,71 @@ procedure TALSStreamBufferSound.Update(const aElapsedTime: single);
bufid: ALuint;
readCount: sf_count_t;
bufferIndex: integer;
+ res: ALenum;
begin
inherited Update(aElapsedTime);
if Error then
exit;
- // Get the number of processed buffer
- alGetSourceiv(FSource, AL_BUFFERS_PROCESSED, @processed);
- if processed < 1 then
- exit;
+ EnterCS;
+ try
+ // Get the number of processed buffer
+ alGetSourceiv(FSource, AL_BUFFERS_PROCESSED, @processed);
+ if processed < 1 then
+ exit;
- FFrameReadAccu := FFrameReadAccu + int64(processed * FBufferFrameCount);
+ FFrameReadAccu := FFrameReadAccu + int64(processed * FBufferFrameCount);
- // Unqueue and fill each processed buffer
- while (processed > 0) do
- begin
- alSourceUnqueueBuffers(FSource, 1, @bufid);
- Dec(processed);
-
- // increment the index of the played buffer. we use this index to retrieve
- // the channel's level.
- inc(FPlayedBufferIndex);
- if FPlayedBufferIndex >= FUsedBuffer then
- FPlayedBufferIndex := 0;
-
- // retrieves the index of the buffer to refill with audio
- bufferIndex := 0;
- while FBuffers[bufferIndex].BufferID <> bufid do
- inc(bufferIndex);
-
- // Read data from opened file
- readCount := FDoReadFromStream(FBuffers[bufferIndex].Data,
- FBuffers[bufferIndex].FrameCapacity);
- FBuffers[bufferIndex].FrameCount := readCount;
-
- if readCount > 0 then
+ // Unqueue and fill each processed buffer
+ while (processed > 0) do
begin
- // callback OnNewBuffer
- if FOnCustomDSP <> NIL then
- FOnCustomDSP(Self, FBuffers[bufferIndex]);
- // refill the openAL buffer with...
- alBufferData(bufid, ALenum(FFormatForAL), FBuffers[bufferIndex].Data,
- ALsizei(readCount * FFrameSize), ALsizei(Fsfinfo.SampleRate));
- // and queue it back on the source
- alSourceQueueBuffers(FSource, 1, @bufid);
- // Set the opened file read cursor to the beginning if LOOP mode is enabled,
- // and the buffer was not completely filled
- if FLoop and (readCount < FBuffers[bufferIndex].FrameCapacity) then
+ alSourceUnqueueBuffers(FSource, 1, @bufid);
+ Dec(processed);
+ res := alGetError();
+ if res <> AL_NO_ERROR then continue;
+
+ // increment the index of the played buffer. we use this index to retrieve
+ // the channel's level.
+ inc(FPlayedBufferIndex);
+ if FPlayedBufferIndex >= FUsedBuffer then
+ FPlayedBufferIndex := 0;
+
+ // retrieves the index of the buffer to refill with audio
+ bufferIndex := 0;
+ while FBuffers[bufferIndex].BufferID <> bufid do
+ inc(bufferIndex);
+
+ // Read data from opened file
+ readCount := FDoReadFromStream(FBuffers[bufferIndex].Data,
+ FBuffers[bufferIndex].FrameCapacity);
+ FBuffers[bufferIndex].FrameCount := readCount;
+
+ if readCount > 0 then
begin
- sf_seek(Fsndfile, 0, SF_SEEK_SET);
- FFrameReadAccu := 0;
- end;
+ // callback custom DSP
+ if FOnCustomDSP <> NIL then
+ FOnCustomDSP(Self, FBuffers[bufferIndex], FOnCustomDSPUserData);
+ // refill the openAL buffer with...
+ alBufferData(bufid, ALenum(FFormatForAL), FBuffers[bufferIndex].Data,
+ ALsizei(readCount * FFrameSize), ALsizei(Fsfinfo.SampleRate));
+ // and queue it back on the source
+ alSourceQueueBuffers(FSource, 1, @bufid);
+ // Set the opened file read cursor to the beginning if LOOP mode is enabled,
+ // and the buffer was not completely filled
+ if FLoop and (readCount < FBuffers[bufferIndex].FrameCapacity) then
+ begin
+ sf_seek(Fsndfile, 0, SF_SEEK_SET);
+ FFrameReadAccu := 0;
+ end;
- // retrieve the channels level
- if FMonitoringEnabled then
- FBuffers[bufferIndex].ComputeChannelsLevel;
+ // retrieve the channels level
+ if FMonitoringEnabled then
+ FBuffers[bufferIndex].ComputeChannelsLevel;
+ end;
end;
+ finally
+ LeaveCS;
end;
end;
@@ -3969,7 +3898,7 @@ procedure TALSStreamBufferSound.InternalRewind;
constructor TALSStreamBufferSound.CreateFromFile(aParent: TALSPlaybackContext;
const aFilename: string; aEnableMonitor: boolean;
- aOnCustomDSP: TALSOnCustomDSP);
+ aOnCustomDSP: TALSOnCustomDSP; aCustomDSPUserData: Pointer);
var
fileopened: boolean;
begin
@@ -3980,16 +3909,13 @@ constructor TALSStreamBufferSound.CreateFromFile(aParent: TALSPlaybackContext;
FFilename := aFilename;
FMonitoringEnabled := aEnableMonitor;
FOnCustomDSP := aOnCustomDSP;
+ FOnCustomDSPUserData := aCustomDSPUserData;
FDoReadFromStream := @DoReadStreamFromFile;
if not Error then
begin
- {$ifdef windows}
- Fsndfile := sf_wchar_open(PWideChar(UnicodeString(aFilename)), SFM_READ, @Fsfinfo);
- {$else}
- Fsndfile := sf_open(PChar(aFilename), SFM_READ, @Fsfinfo);
- {$endif}
+ Fsndfile := ALSOpenAudioFile(aFilename, SFM_READ, @Fsfinfo);
if Fsndfile = nil then
SetError(als_FileNotOpened)
else
@@ -4073,6 +3999,7 @@ procedure TALSStreamBufferSound.SetTimePosition(AValue: single);
i: integer;
begin
if Error then exit;
+ if TotalDuration = 0 then exit;
if (AValue < 0) or (AValue > TotalDuration) then exit;
LockContext( FParentContext.FContext );
@@ -4158,7 +4085,8 @@ function TALSSingleStaticBufferSound.GetChannelLevel(index: integer): single;
end;
constructor TALSSingleStaticBufferSound.CreateFromFile(aParent: TALSPlaybackContext;
- const aFilename: string; aEnableMonitor: boolean; aOnCustomDSP: TALSOnCustomDSP);
+ const aFilename: string; aEnableMonitor: boolean; aOnCustomDSP: TALSOnCustomDSP;
+ aCustomDSPUserData: Pointer);
var
sndfile: PSNDFILE;
sfinfo: TSF_INFO;
@@ -4172,14 +4100,11 @@ constructor TALSSingleStaticBufferSound.CreateFromFile(aParent: TALSPlaybackCont
FFilename := aFilename;
FMonitoringEnabled := aEnableMonitor;
FOnCustomDSP := aOnCustomDSP;
+ FOnCustomDSPUserData := aCustomDSPUserData;
if not Error then
begin
- {$ifdef windows}
- sndfile := sf_wchar_open(PWideChar(UnicodeString(aFilename)), SFM_READ, @sfinfo);
- {$else}
- sndfile := sf_open(PChar(aFilename), SFM_READ, @sfinfo);
- {$endif}
+ sndfile := ALSOpenAudioFile(aFilename, SFM_READ, @sfinfo);
if sndfile = nil then
SetError(als_FileNotOpened)
else
@@ -4218,7 +4143,7 @@ constructor TALSSingleStaticBufferSound.CreateFromFile(aParent: TALSPlaybackCont
FByteCount := frameRead*FFrameSize;
end;
if FOnCustomDSP <> NIL then
- FOnCustomDSP(Self, FBuffers[0]);
+ FOnCustomDSP(Self, FBuffers[0], FOnCustomDSPUserData);
end;
end;
end;
@@ -4258,13 +4183,14 @@ constructor TALSSingleStaticBufferSound.CreateFromFile(aParent: TALSPlaybackCont
// White noise generation from OpenAL example "altonegen.c"
constructor TALSSingleStaticBufferSound.CreateWhiteNoise(aParent: TALSPlaybackContext;
aDuration: single; aChannelCount: integer; aEnableMonitor: boolean;
- aOnCustomDSP: TALSOnCustomDSP);
+ aOnCustomDSP: TALSOnCustomDSP; aCustomDSPUserData: Pointer);
begin
FParentContext := aParent;
InitializeErrorStatus;
FFilename := '';
FMonitoringEnabled := aEnableMonitor;
FOnCustomDSP := aOnCustomDSP;
+ FOnCustomDSPUserData := aCustomDSPUserData;
if not Error then
begin
@@ -4304,7 +4230,7 @@ constructor TALSSingleStaticBufferSound.CreateWhiteNoise(aParent: TALSPlaybackCo
dsp_FillWithWhiteNoise_Smallint(FBuffers[0].Data, FFrameCount, FBuffers[0].ChannelCount);
if FOnCustomDSP <> NIL then
- FOnCustomDSP(Self, FBuffers[0]);
+ FOnCustomDSP(Self, FBuffers[0], FOnCustomDSPUserData);
alBufferData(FBuffers[0].BufferID, FFormatForAL, FBuffers[0].Data, FByteCount, FSampleRate);
CheckALError(als_ALCanNotFillBuffer);
@@ -4421,12 +4347,13 @@ destructor TALSPlaybackContext.Destroy;
end;
function TALSPlaybackContext.AddStream(const aFilename: string; aEnableMonitoring: boolean;
- aOnCustomDSP: TALSOnCustomDSP): TALSSound;
+ aOnCustomDSP: TALSOnCustomDSP; aCustomDSPUserData: Pointer): TALSSound;
begin
EnterCriticalSection(FCriticalSection);
LockContext( FContext );
try
- Result := TALSStreamBufferSound.CreateFromFile(Self, aFilename, aEnableMonitoring, aOnCustomDSP);
+ Result := TALSStreamBufferSound.CreateFromFile(Self, aFilename,
+ aEnableMonitoring, aOnCustomDSP, aCustomDSPUserData);
FList.Add(Result);
finally
LeaveCriticalSection(FCriticalSection);
@@ -4435,13 +4362,14 @@ function TALSPlaybackContext.AddStream(const aFilename: string; aEnableMonitorin
end;
function TALSPlaybackContext.CreateWhiteNoise(aDuration: single;
- aChannelCount: integer; aEnableMonitoring: boolean; aOnCustomDSP: TALSOnCustomDSP): TALSSound;
+ aChannelCount: integer; aEnableMonitoring: boolean;
+ aOnCustomDSP: TALSOnCustomDSP; aCustomDSPUserData: Pointer): TALSSound;
begin
EnterCriticalSection(FCriticalSection);
LockContext( FContext );
try
Result := TALSSingleStaticBufferSound.CreateWhiteNoise(Self, aDuration,
- aChannelCount, aEnableMonitoring, aOnCustomDSP);
+ aChannelCount, aEnableMonitoring, aOnCustomDSP, aCustomDSPUserData);
FList.Add(Result);
finally
LeaveCriticalSection(FCriticalSection);
@@ -4452,7 +4380,6 @@ function TALSPlaybackContext.CreateWhiteNoise(aDuration: single;
procedure TALSPlaybackContext.DoUpdate(const aElapsedTime: single);
var
i: integer;
- s: TALSSound;
begin
EnterCriticalSection(FCriticalSection);
FThreadIsStarted:=True;
@@ -4475,11 +4402,11 @@ procedure TALSPlaybackContext.DoUpdate(const aElapsedTime: single);
// Kill or update sounds
for i := FList.Count - 1 downto 0 do
begin
- s := TALSSound(FList.Items[i]);
- if s.FKill then
+ FSoundToProcess := TALSSound(FList.Items[i]);
+ if FSoundToProcess.FKill then
InternalDeleteSound(i)
else
- s.Update(aElapsedTime);
+ FSoundToProcess.Update(aElapsedTime);
end;
// update playlist (if exists)
if FPlaylist <> nil then
@@ -4497,6 +4424,12 @@ procedure TALSPlaybackContext.DoUpdate(const aElapsedTime: single);
end;
end;
+procedure TALSPlaybackContext.DoSoundOnStopped;
+begin
+ if FSoundToProcess.FOnStopped <> NIL then
+ FSoundToProcess.FOnStopped(FSoundToProcess);
+end;
+
procedure TALSPlaybackContext.EnterCS;
begin
EnterCriticalSection(FCriticalSection);
@@ -4542,7 +4475,7 @@ function TALSPlaybackContext.CreateEffect(aEffectType: TALSEffectType;
begin
Result.InitDefault(Self);
- if not ALSManager.Error and FHaveEXT_ALC_EXT_EFX then
+ if not ALSManager.Error and FParentDeviceItem^.FHaveEXT_ALC_EXT_EFX then
begin
LockContext( FContext );
try
@@ -4611,7 +4544,7 @@ function TALSPlaybackContext.ChangeAttributes(const aAttribs: TALSContextAttribu
begin
LockContext( FContext );
try
- Result := alcResetDeviceSOFT( FParentDevice, @aAttribs );
+ Result := FParentDeviceItem^.alcResetDeviceSOFT( FParentDevice, @aAttribs );
finally
UnlockContext;
end;
@@ -4731,10 +4664,20 @@ function TALSPlaybackContext.GetHRTFEnabled: boolean;
function TALSPlaybackContext.GetHaveFilter: boolean;
begin
- Result := FHaveEXT_ALC_EXT_EFX and
+ Result := FParentDeviceItem^.FHaveEXT_ALC_EXT_EFX and
(FHaveLowPassFilter or FHaveBandPassFilter or FHaveHighPassFilter);
end;
+function TALSPlaybackContext.GetHaveEXT_ALC_EXT_EFX: boolean;
+begin
+ Result := FParentDeviceItem^.FHaveEXT_ALC_EXT_EFX;
+end;
+
+function TALSPlaybackContext.GetHaveExt_ALC_SOFT_HRTF: boolean;
+begin
+ Result := FParentDeviceItem^.FHaveExt_ALC_SOFT_HRTF;
+end;
+
function TALSPlaybackContext.GetHRTFList: TStringArray;
var
num_hrtf: ALCint;
@@ -4750,7 +4693,7 @@ function TALSPlaybackContext.GetHRTFList: TStringArray;
alcGetIntegerv(FParentDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, @num_hrtf);
SetLength(Result, num_hrtf);
for i:=0 to num_hrtf-1 do
- Result[i] := alcGetStringiSOFT(FParentDevice, ALC_HRTF_SPECIFIER_SOFT, ALCSizei(i));
+ Result[i] := FParentDeviceItem^.alcGetStringiSOFT(FParentDevice, ALC_HRTF_SPECIFIER_SOFT, ALCSizei(i));
end;
finally
UnlockContext;
@@ -4846,31 +4789,31 @@ procedure TALSPlaybackContext.InitializeALContext(
// Look for some extensions before the creation of the context because
// some attributes use them.
- FHaveEXT_ALC_EXT_EFX := alcIsExtensionPresent(FParentDevice, PChar('ALC_EXT_EFX'));
+{ FHaveEXT_ALC_EXT_EFX := alcIsExtensionPresent(FParentDevice, PChar('ALC_EXT_EFX'));
if FHaveEXT_ALC_EXT_EFX then
FHaveEXT_ALC_EXT_EFX := LoadExt_ALC_EXT_EFX;
FHaveExt_ALC_SOFT_HRTF := alcIsExtensionPresent(FParentDevice, PChar('ALC_SOFT_HRTF'));
if FHaveExt_ALC_SOFT_HRTF then
- FHaveExt_ALC_SOFT_HRTF := LoadExt_ALC_SOFT_HRTF(FParentDevice);
+ FHaveExt_ALC_SOFT_HRTF := LoadExt_ALC_SOFT_HRTF(FParentDevice); }
FHaveExt_AL_SOFT_source_resampler := alcIsExtensionPresent(FParentDevice, PChar('AL_SOFT_source_resampler'));
if FHaveExt_AL_SOFT_source_resampler then
FHaveExt_AL_SOFT_source_resampler := LoadExt_AL_SOFT_source_resampler;
- FHaveExt_ALC_SOFT_output_mode := alcIsExtensionPresent(FParentDevice, PChar('ALC_SOFT_output_mode'));
+// FHaveExt_ALC_SOFT_output_mode := alcIsExtensionPresent(FParentDevice, PChar('ALC_SOFT_output_mode'));
- FHaveExt_ALC_SOFT_loopback := alcIsExtensionPresent(FParentDevice, PChar('ALC_SOFT_loopback'));
+// FHaveExt_ALC_SOFT_loopback := alcIsExtensionPresent(FParentDevice, PChar('ALC_SOFT_loopback'));
// extension is loaded in TALSLoopbackDeviceItem.Open
- FHaveExt_ALC_SOFT_output_limiter := alcIsExtensionPresent(FParentDevice, PChar('ALC_SOFT_output_limiter'));
+// FHaveExt_ALC_SOFT_output_limiter := alcIsExtensionPresent(FParentDevice, PChar('ALC_SOFT_output_limiter'));
FContext := alcCreateContext(FParentDevice,
- @aAttribs.ToArray(FHaveEXT_ALC_EXT_EFX,
- FHaveExt_ALC_SOFT_output_mode,
- FHaveExt_ALC_SOFT_HRTF,
- FHaveExt_ALC_SOFT_loopback,
- FHaveExt_ALC_SOFT_output_limiter)[0]);
+ @aAttribs.ToArray(FParentDeviceItem^.FHaveEXT_ALC_EXT_EFX,
+ FParentDeviceItem^.FHaveExt_ALC_SOFT_output_mode,
+ FParentDeviceItem^.FHaveExt_ALC_SOFT_HRTF,
+ FParentDeviceItem^.FHaveExt_ALC_SOFT_loopback,
+ FParentDeviceItem^.FHaveExt_ALC_SOFT_output_limiter)[0]);
CheckALCError(FParentDevice, als_ALContextNotCreated);
if FContext <> nil then
@@ -4897,7 +4840,7 @@ procedure TALSPlaybackContext.InitializeALContext(
alListenerfv(AL_ORIENTATION, @A[0]);
//check for available filter
- if FHaveEXT_ALC_EXT_EFX then
+ if FParentDeviceItem^.FHaveEXT_ALC_EXT_EFX then
begin
alGenFilters(1, @obj);
alGetError();
@@ -4998,14 +4941,16 @@ function TALSPlaybackContext.AddCapturePlayback(aSampleRate: integer;
end;
end;
-constructor TALSPlaybackContext.Create(aDevice: PALCDevice; const aAttribs: TALSContextAttributes);
+constructor TALSPlaybackContext.Create(aDevice: PALSPlaybackDeviceItem;
+ const aAttribs: TALSContextAttributes);
begin
FExecutingConstructor := True;
InitializeErrorStatus;
- FParentDevice := aDevice;
+ FParentDeviceItem := PALSDeviceItem(aDevice);
+ FParentDevice := aDevice^.Handle;
FObtainedSampleRate := aAttribs.SampleRate;
- if aDevice = NIL then
+ if aDevice^.Handle = NIL then
SetError(als_ALCanNotOpenPlaybackDevice)
else
InitializeALContext(aAttribs);
@@ -5015,6 +4960,7 @@ constructor TALSPlaybackContext.Create(aDevice: PALCDevice; const aAttribs: TALS
InitCriticalSection( FCriticalSection );
FThreadIsStarted:=False;
FThread := TALSThread.Create(@DoUpdate, 10, True);
+ FThread.Priority := tpHighest;
// waits for the thread to be started to prevent any problems
while not FThreadIsStarted do
Sleep(1);
@@ -5022,14 +4968,15 @@ constructor TALSPlaybackContext.Create(aDevice: PALCDevice; const aAttribs: TALS
end;
function TALSPlaybackContext.AddSound(const aFilename: string; aEnableMonitoring: boolean;
- aOnCustomDSP: TALSOnCustomDSP): TALSSound;
+ aOnCustomDSP: TALSOnCustomDSP; aCustomDSPUserData: Pointer): TALSSound;
begin
LockContext( FContext );
try
EnterCriticalSection(FCriticalSection);
try
- Result := TALSSingleStaticBufferSound.CreateFromFile(Self, aFilename, aEnableMonitoring, aOnCustomDSP);
+ Result := TALSSingleStaticBufferSound.CreateFromFile(Self, aFilename,
+ aEnableMonitoring, aOnCustomDSP, aCustomDSPUserData);
FList.Add(Result);
finally
LeaveCriticalSection(FCriticalSection);
@@ -5108,7 +5055,7 @@ procedure TALSSound.CreateParameters;
Tone.FOnLockParam := @EnterCS;
Tone.FOnUnlockParam := @LeaveCS;
- if not Error and FParentContext.FHaveEXT_ALC_EXT_EFX then
+ if not Error and FParentContext.FParentDeviceItem^.FHaveEXT_ALC_EXT_EFX then
begin
SetLength(FAuxiliarySend, FParentContext.ObtainedAuxiliarySendCount);
for i := 0 to High(FAuxiliarySend) do
@@ -5135,11 +5082,22 @@ procedure TALSSound.FreeParameters;
FAuxiliarySend[i].Disconnect;
end;
-procedure TALSSound.SetOnCustomDSP(AValue: TALSOnCustomDSP);
+procedure TALSSound.SetOnCustomDSP(aProc: TALSOnCustomDSP; aUserData: Pointer);
begin
EnterCS;
try
- FOnCustomDSP := AValue;
+ FOnCustomDSP := aProc;
+ FOnCustomDSPUserData := aUserData;
+ finally
+ LeaveCS;
+ end;
+end;
+
+procedure TALSSound.SetOnStopped(AValue: TALSNotifyEvent);
+begin
+ EnterCS;
+ try
+ FOnStopped := AValue;
finally
LeaveCS;
end;
@@ -5536,7 +5494,7 @@ function TALSSound.GetChannelLevel(index: integer): single;
function TALSSound.GetChannelLeveldB(index: integer): single;
begin
- Result := ALSPercentToDecibel( GetChannelLevel(index) );
+ Result := LinearTodB( GetChannelLevel(index) );
end;
procedure TALSSound.SetMute(AValue: boolean);
@@ -5672,7 +5630,10 @@ procedure TALSSound.LeaveCS;
procedure TALSSound.Update(const aElapsedTime: single);
var
v: single;
+ flagDoOnStopped: boolean;
begin
+ flagDoOnStopped := False;
+
EnterCriticalSection(FCriticalSection);
try
v := Volume.Value;
@@ -5697,9 +5658,16 @@ procedure TALSSound.Update(const aElapsedTime: single);
FKill := FKill or FKillAfterFadeOut;
end;
+ flagDoOnStopped := (State = ALS_STOPPED) and
+ (FPreviousState <> ALS_STOPPED);
+ FPreviousState := State;
finally
LeaveCriticalSection(FCriticalSection);;
end;
+
+ if flagDoOnStopped and (FOnStopped <> NIL) then
+ FParentContext.FThread.Synchronize(FParentContext.FThread,
+ @FParentContext.DoSoundOnStopped);
end;
procedure TALSSound.InternalRewind;
diff --git a/source/libsndfile.pas b/source/libsndfile.pas
index 81ba580..a27b931 100644
--- a/source/libsndfile.pas
+++ b/source/libsndfile.pas
@@ -906,6 +906,9 @@ TSF_CHUNK_ITERATOR = record
function LoadSndFileLibrary( const aFilename: string ): boolean;
procedure UnloadSndFileLibrary;
+// open an audio file in a cross platform way
+function ALSOpenAudioFile(const aFilename: string; aMode: cint; Asfinfo: PSF_INFO): PSNDFILE;
+
implementation
@@ -1010,4 +1013,15 @@ procedure UnloadSndFileLibrary;
end;
end;
+function ALSOpenAudioFile(const aFilename: string; aMode: cint; Asfinfo: PSF_INFO): PSNDFILE;
+begin
+ {$ifdef windows}
+ Result := sf_wchar_open(PWideChar(UnicodeString(aFilename)), aMode, Asfinfo);
+ {$else}
+ Result := sf_open(PChar(aFilename), aMode, Asfinfo);
+ {$endif}
+end;
+
+
+
end.
diff --git a/source/openalsoft.pas b/source/openalsoft.pas
index bc9a15e..24b1374 100644
--- a/source/openalsoft.pas
+++ b/source/openalsoft.pas
@@ -63,21 +63,23 @@ interface
function LoadOpenALCoreLibrary( const aFilename: string ): boolean;
procedure UnloadOpenALSoftLibrary;
-// load extension
-function LoadExt_ALC_EXT_EFX: boolean; // loaded in playback/loopback context
+// Device dependant extensions
function LoadExt_ALC_EXT_thread_local_context(aDevice: PALCDevice): boolean;
+function LoadExt_ALC_SOFT_loopback(aDevice: PALCDevice): boolean;
+function LoadExt_ALC_SOFT_pause_device(aDevice: PALCDevice): boolean;
+function LoadExt_ALC_SOFT_HRTF(aDevice: PALCDevice): boolean; // loaded in playback/loopback context
+function LoadExt_ALC_SOFT_device_clock(aDevice: PALCDevice): boolean;
+function LoadExt_ALC_SOFT_reopen_device(aDevice: PALCDevice): boolean;
+
+// Other extensions
+function LoadExt_ALC_EXT_EFX: boolean; // loaded in playback/loopback context
function LoadExt_AL_SOFT_buffer_samples: boolean;
function LoadExt_AL_SOFT_buffer_sub_data: boolean;
function LoadExt_AL_SOFT_callback_buffer: boolean;
-function LoadExt_ALC_SOFT_loopback(aDevice: PALCDevice): boolean;
function LoadExt_AL_SOFT_source_latency: boolean;
function LoadExt_AL_SOFT_deferred_updates: boolean; // loaded in playback/loopback context
-function LoadExt_ALC_SOFT_pause_device(aDevice: PALCDevice): boolean;
-function LoadExt_ALC_SOFT_HRTF(aDevice: PALCDevice): boolean; // loaded in playback/loopback context
function LoadExt_AL_SOFT_source_resampler: boolean; // loaded in playback/loopback context
-function LoadExt_ALC_SOFT_device_clock(aDevice: PALCDevice): boolean;
function LoadExt_AL_SOFT_events: boolean;
-function LoadExt_ALC_SOFT_reopen_device(aDevice: PALCDevice): boolean;
// some functions to ease things
function GetDeviceNames: TStringArray;
@@ -94,18 +96,12 @@ function StringToNullTerminated(const s: string): PChar;
var
FLoaded_OpenALCore_: boolean=False;
FExtensionLoaded_ALC_EXT_EFX: boolean=False;
- FExtensionLoaded_ALC_EXT_thread_local_context: boolean=False;
FExtensionLoaded_AL_SOFT_buffer_samples: boolean=False;
FExtensionLoaded_AL_SOFT_buffer_sub_data: boolean=False;
FExtensionLoaded_AL_SOFT_callback_buffer: boolean=False;
- FExtensionLoaded_ALC_SOFT_loopback: boolean=False;
- FExtensionLoaded_ALC_SOFT_reopen_device: boolean=False;
FExtensionLoaded_AL_SOFT_source_latency: boolean=False;
FExtensionLoaded_AL_SOFT_deferred_updates: boolean=False;
- FExtensionLoaded_ALC_SOFT_pause_device: boolean=False;
- FExtensionLoaded_ALC_SOFT_HRTF: boolean=False;
FExtensionLoaded_AL_SOFT_source_resampler: boolean=False;
- FExtensionLoaded_ALC_SOFT_device_clock: boolean=False;
FExtensionLoaded_AL_SOFT_events: boolean=False;
implementation
@@ -329,17 +325,17 @@ function LoadExt_ALC_EXT_EFX: boolean;
function LoadExt_ALC_EXT_thread_local_context(aDevice: PALCDevice): boolean;
begin
- Result := FExtensionLoaded_ALC_EXT_thread_local_context;
- if Result then exit;
- if _OpenALLib_Handle <> DynLibs.NilHandle then
+ Result := alcIsExtensionPresent(NIL, PChar('ALC_EXT_thread_local_context'));
+ if Result then
begin
- FExtensionLoaded_ALC_EXT_thread_local_context := True;
- Pointer(alcSetThreadContext) := GetALCExtProc(aDevice, 'alcSetThreadContext', FExtensionLoaded_ALC_EXT_thread_local_context);
- Pointer(alcGetThreadContext) := GetALCExtProc(aDevice, 'alcGetThreadContext', FExtensionLoaded_ALC_EXT_thread_local_context);
- Result := FExtensionLoaded_ALC_EXT_thread_local_context;
+ Pointer(_alcSetThreadContext) := GetALCExtProc(aDevice, 'alcSetThreadContext', Result);
+ Pointer(_alcGetThreadContext) := GetALCExtProc(aDevice, 'alcGetThreadContext', Result);
end
else
- Result := False;
+ begin
+ _alcSetThreadContext := NIL;
+ _alcGetThreadContext := NIL;
+ end;
end;
function LoadExt_AL_SOFT_buffer_samples: boolean;
@@ -393,18 +389,19 @@ function LoadExt_AL_SOFT_callback_buffer: boolean;
function LoadExt_ALC_SOFT_loopback(aDevice: PALCDevice): boolean;
begin
- Result := FExtensionLoaded_ALC_SOFT_loopback;
- if Result then exit;
- if _OpenALLib_Handle <> DynLibs.NilHandle then
+ Result := alcIsExtensionPresent(aDevice, PChar('ALC_SOFT_loopback'));
+ if Result then
begin
- FExtensionLoaded_ALC_SOFT_loopback := True;
- Pointer(alcLoopbackOpenDeviceSOFT) := GetALCExtProc(aDevice, 'alcLoopbackOpenDeviceSOFT', FExtensionLoaded_ALC_SOFT_loopback);
- Pointer(alcIsRenderFormatSupportedSOFT) := GetALCExtProc(aDevice, 'alcIsRenderFormatSupportedSOFT', FExtensionLoaded_ALC_SOFT_loopback);
- Pointer(alcRenderSamplesSOFT) := GetALCExtProc(aDevice, 'alcRenderSamplesSOFT', FExtensionLoaded_ALC_SOFT_loopback);
- Result := FExtensionLoaded_ALC_SOFT_loopback;
+ Pointer(_alcLoopbackOpenDeviceSOFT) := GetALCExtProc(aDevice, 'alcLoopbackOpenDeviceSOFT', Result);
+ Pointer(_alcIsRenderFormatSupportedSOFT) := GetALCExtProc(aDevice, 'alcIsRenderFormatSupportedSOFT', Result);
+ Pointer(_alcRenderSamplesSOFT) := GetALCExtProc(aDevice, 'alcRenderSamplesSOFT', Result);
end
else
- Result := False;
+ begin
+ _alcLoopbackOpenDeviceSOFT := NIL;
+ _alcIsRenderFormatSupportedSOFT := NIL;
+ _alcRenderSamplesSOFT := NIL;
+ end;
end;
function LoadExt_AL_SOFT_source_latency: boolean;
@@ -449,32 +446,32 @@ function LoadExt_AL_SOFT_deferred_updates: boolean;
function LoadExt_ALC_SOFT_pause_device(aDevice: PALCDevice): boolean;
begin
- Result := FExtensionLoaded_ALC_SOFT_pause_device;
- if Result then exit;
- if _OpenALLib_Handle <> DynLibs.NilHandle then
+ Result := alcIsExtensionPresent(NIL, PChar('ALC_SOFT_pause_device'));
+ if Result then
begin
- FExtensionLoaded_ALC_SOFT_pause_device := True;
- Pointer(alcDevicePauseSOFT) := GetALCExtProc(aDevice, 'alcDevicePauseSOFT', FExtensionLoaded_ALC_SOFT_pause_device);
- Pointer(alcDeviceResumeSOFT) := GetALCExtProc(aDevice, 'alcDeviceResumeSOFT', FExtensionLoaded_ALC_SOFT_pause_device);
- Result := FExtensionLoaded_ALC_SOFT_pause_device;
+ Pointer(_alcDevicePauseSOFT) := GetALCExtProc(aDevice, 'alcDevicePauseSOFT', Result);
+ Pointer(_alcDeviceResumeSOFT) := GetALCExtProc(aDevice, 'alcDeviceResumeSOFT', Result);
end
else
- Result := False;
+ begin
+ _alcDevicePauseSOFT := NIL;
+ _alcDeviceResumeSOFT := NIL;
+ end;
end;
function LoadExt_ALC_SOFT_HRTF(aDevice: PALCDevice): boolean;
begin
- Result := FExtensionLoaded_ALC_SOFT_HRTF;
- if Result then exit;
- if _OpenALLib_Handle <> DynLibs.NilHandle then
+ Result := alcIsExtensionPresent(aDevice, PChar('ALC_SOFT_HRTF'));
+ if Result then
begin
- FExtensionLoaded_ALC_SOFT_HRTF := True;
- Pointer(alcGetStringiSOFT) := GetALCExtProc(aDevice, 'alcGetStringiSOFT', FExtensionLoaded_ALC_SOFT_HRTF);
- Pointer(alcResetDeviceSOFT) := GetALCExtProc(aDevice, 'alcResetDeviceSOFT', FExtensionLoaded_ALC_SOFT_HRTF);
- Result := FExtensionLoaded_ALC_SOFT_HRTF;
+ Pointer(_alcGetStringiSOFT) := GetALCExtProc(aDevice, 'alcGetStringiSOFT', Result);
+ Pointer(_alcResetDeviceSOFT) := GetALCExtProc(aDevice, 'alcResetDeviceSOFT', Result);
end
else
- Result := False;
+ begin
+ _alcGetStringiSOFT := NIL;
+ _alcResetDeviceSOFT := NIL;
+ end;
end;
function LoadExt_AL_SOFT_source_resampler: boolean;
@@ -493,16 +490,15 @@ function LoadExt_AL_SOFT_source_resampler: boolean;
function LoadExt_ALC_SOFT_device_clock(aDevice: PALCDevice): boolean;
begin
- Result := FExtensionLoaded_ALC_SOFT_device_clock;
- if Result then exit;
- if _OpenALLib_Handle <> DynLibs.NilHandle then
+ Result := alcIsExtensionPresent(NIL, PChar('ALC_SOFT_device_clock'));
+ if Result then
begin
- FExtensionLoaded_ALC_SOFT_device_clock := true;
- Pointer(alcGetInteger64vSOFT) := GetALCExtProc(aDevice, 'alcGetInteger64vSOFT', FExtensionLoaded_ALC_SOFT_device_clock);
- Result := FExtensionLoaded_ALC_SOFT_device_clock;
+ Pointer(_alcGetInteger64vSOFT) := GetALCExtProc(aDevice, 'alcGetInteger64vSOFT', Result);
end
else
- Result := False;
+ begin
+ _alcGetInteger64vSOFT := NIL;
+ end;
end;
function LoadExt_AL_SOFT_events: boolean;
@@ -524,16 +520,15 @@ function LoadExt_AL_SOFT_events: boolean;
function LoadExt_ALC_SOFT_reopen_device(aDevice: PALCDevice): boolean;
begin
- Result := FExtensionLoaded_ALC_SOFT_reopen_device;
- if Result then exit;
- if _OpenALLib_Handle <> DynLibs.NilHandle then
+ Result := alcIsExtensionPresent(NIL, PChar('ALC_SOFT_reopen_device'));
+ if Result then
begin
- FExtensionLoaded_ALC_SOFT_reopen_device := True;
- Pointer(alcReopenDeviceSOFT) := GetALCExtProc(aDevice, 'alcReopenDeviceSOFT', FExtensionLoaded_ALC_SOFT_reopen_device);
- Result := FExtensionLoaded_ALC_SOFT_reopen_device;
+ Pointer(_alcReopenDeviceSOFT) := GetALCExtProc(aDevice, 'alcReopenDeviceSOFT', Result);
end
else
- Result := False;
+ begin
+ _alcReopenDeviceSOFT := NIL;
+ end;
end;