From 15478713661929c8e6687a6662e1892b51f0e2e1 Mon Sep 17 00:00:00 2001 From: Lulu Date: Thu, 29 Dec 2022 08:30:05 +0100 Subject: [PATCH 01/25] TALSFrameBufferBase.FreeMemory moved to public section --- source/als_frame_buffers.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/als_frame_buffers.inc b/source/als_frame_buffers.inc index a715425..471cd46 100644 --- a/source/als_frame_buffers.inc +++ b/source/als_frame_buffers.inc @@ -28,11 +28,11 @@ 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 Amplify(aGain: single); From f2461c256043955bf1da4848e8cfb5fc88f24468 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sat, 31 Dec 2022 14:16:51 +0100 Subject: [PATCH 02/25] Open audio file in a cross platform way --- source/alsound.pas | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/source/alsound.pas b/source/alsound.pas index 1e8ea11..89cb4ec 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -1354,6 +1354,15 @@ procedure UnlockContext; {$endif} end; +function SFL_OpenAudioFile(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; + {$undef ALS_INTERFACE} {$define ALS_IMPLEMENTATION} @@ -1523,9 +1532,9 @@ 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); + FFile := SFL_OpenAudioFile(aFilename, SFM_WRITE, @FFileInfo); { if FFile = NIL then SetMixingError(als_CanNotCreateTargetMixFile); } @@ -2064,7 +2073,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 := SFL_OpenAudioFile(FTempFileName, SFM_WRITE, @tempInfo); if FTempFile = nil then FCaptureError := als_CanNotOpenTemporaryCaptureFile; @@ -2166,14 +2175,14 @@ procedure TALSCaptureContext.StopCapture; begin // reopen the temporary '.au' file for reading fileInfo.Format := 0; - FTempFile := sf_open(PChar(FTempFileName), SFM_READ, @fileInfo); + FTempFile := SFL_OpenAudioFile(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 := SFL_OpenAudioFile(FUserFileName, SFM_WRITE, @FUserFileInfo); if finalFile = nil then begin SetCaptureError(als_CanNotCreateTargetCaptureFile); @@ -3985,11 +3994,7 @@ constructor TALSStreamBufferSound.CreateFromFile(aParent: TALSPlaybackContext; 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 := SFL_OpenAudioFile(aFilename, SFM_READ, @Fsfinfo); if Fsndfile = nil then SetError(als_FileNotOpened) else @@ -4175,11 +4180,7 @@ constructor TALSSingleStaticBufferSound.CreateFromFile(aParent: TALSPlaybackCont 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 := SFL_OpenAudioFile(aFilename, SFM_READ, @sfinfo); if sndfile = nil then SetError(als_FileNotOpened) else From 3a67d0d10ba2d73d142ee9c1c2c6e7b06caba793 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sat, 31 Dec 2022 14:40:41 +0100 Subject: [PATCH 03/25] Renamed SFL_OpenAudioFile to ALSOpenAudioFile and moved to libsndfile.pas --- source/alsound.pas | 22 ++++++---------------- source/libsndfile.pas | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/source/alsound.pas b/source/alsound.pas index 89cb4ec..f2773a8 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -1354,16 +1354,6 @@ procedure UnlockContext; {$endif} end; -function SFL_OpenAudioFile(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; - - {$undef ALS_INTERFACE} {$define ALS_IMPLEMENTATION} {$include als_error.inc} @@ -1534,7 +1524,7 @@ function TALSLoopbackContext.PrepareSavingToFile(const aFilename: string; FFileInfo.SampleRate := FSampleRate; FFileInfo. Format := aFileFormat; FFileInfo.Channels := FFrameBuffer.ChannelCount; - FFile := SFL_OpenAudioFile(aFilename, SFM_WRITE, @FFileInfo); + FFile := ALSOpenAudioFile(aFilename, SFM_WRITE, @FFileInfo); { if FFile = NIL then SetMixingError(als_CanNotCreateTargetMixFile); } @@ -2073,7 +2063,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 := SFL_OpenAudioFile(FTempFileName, SFM_WRITE, @tempInfo); + FTempFile := ALSOpenAudioFile(FTempFileName, SFM_WRITE, @tempInfo); if FTempFile = nil then FCaptureError := als_CanNotOpenTemporaryCaptureFile; @@ -2175,14 +2165,14 @@ procedure TALSCaptureContext.StopCapture; begin // reopen the temporary '.au' file for reading fileInfo.Format := 0; - FTempFile := SFL_OpenAudioFile(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 := SFL_OpenAudioFile(FUserFileName, SFM_WRITE, @FUserFileInfo); + finalFile := ALSOpenAudioFile(FUserFileName, SFM_WRITE, @FUserFileInfo); if finalFile = nil then begin SetCaptureError(als_CanNotCreateTargetCaptureFile); @@ -3994,7 +3984,7 @@ constructor TALSStreamBufferSound.CreateFromFile(aParent: TALSPlaybackContext; if not Error then begin - Fsndfile := SFL_OpenAudioFile(aFilename, SFM_READ, @Fsfinfo); + Fsndfile := ALSOpenAudioFile(aFilename, SFM_READ, @Fsfinfo); if Fsndfile = nil then SetError(als_FileNotOpened) else @@ -4180,7 +4170,7 @@ constructor TALSSingleStaticBufferSound.CreateFromFile(aParent: TALSPlaybackCont if not Error then begin - sndfile := SFL_OpenAudioFile(aFilename, SFM_READ, @sfinfo); + sndfile := ALSOpenAudioFile(aFilename, SFM_READ, @sfinfo); if sndfile = nil then SetError(als_FileNotOpened) else 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. From 84881fd2377d1c069ea53b416d6b0790c6d6d8a9 Mon Sep 17 00:00:00 2001 From: Lulu Date: Tue, 10 Jan 2023 14:33:33 +0100 Subject: [PATCH 04/25] Added method to fill buffer with silence --- source/als_frame_buffers.inc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/als_frame_buffers.inc b/source/als_frame_buffers.inc index 471cd46..c7d321d 100644 --- a/source/als_frame_buffers.inc +++ b/source/als_frame_buffers.inc @@ -35,6 +35,7 @@ type 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 From 115cb7c1d9e8a6e9a393e0e768dfe75c9e0b0b10 Mon Sep 17 00:00:00 2001 From: Lulu Date: Mon, 16 Jan 2023 10:54:08 +0100 Subject: [PATCH 05/25] Fixed a bug when setting audio position on empty audio --- source/alsound.pas | 1 + 1 file changed, 1 insertion(+) diff --git a/source/alsound.pas b/source/alsound.pas index f2773a8..29c48ab 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -4068,6 +4068,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 ); From bce6b80063670acf72fe56a176bc1ceaad35fa22 Mon Sep 17 00:00:00 2001 From: Lulu Date: Tue, 17 Jan 2023 10:04:13 +0100 Subject: [PATCH 06/25] Moved TFParam.OnElapse(...) to public section --- source/als_velocity_curve.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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; From 9174d88c8d51cf9514428ef9adcdd2f10bf8cf88 Mon Sep 17 00:00:00 2001 From: Lulu Date: Wed, 18 Jan 2023 10:20:37 +0100 Subject: [PATCH 07/25] Added routines for dB <-> Linear conversion with better names --- source/als_dsp_utils.pas | 20 ++++++++++++++++---- source/als_frame_buffers.inc | 4 ++-- source/alsound.pas | 6 +++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/source/als_dsp_utils.pas b/source/als_dsp_utils.pas index d3ad036..cf8a732 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); @@ -74,12 +76,22 @@ implementation 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; diff --git a/source/als_frame_buffers.inc b/source/als_frame_buffers.inc index c7d321d..9e11243 100644 --- a/source/als_frame_buffers.inc +++ b/source/als_frame_buffers.inc @@ -350,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; @@ -366,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/alsound.pas b/source/alsound.pas index 29c48ab..8ef4b08 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -1919,7 +1919,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; @@ -1934,7 +1934,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); @@ -5528,7 +5528,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); From 698bef613be80f15a8112fe53850527acf26a806 Mon Sep 17 00:00:00 2001 From: Lulu Date: Thu, 19 Jan 2023 08:03:47 +0100 Subject: [PATCH 08/25] Fixed a bug in routine dsp_Mean_Float --- source/als_dsp_utils.pas | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/als_dsp_utils.pas b/source/als_dsp_utils.pas index cf8a732..8d545ac 100644 --- a/source/als_dsp_utils.pas +++ b/source/als_dsp_utils.pas @@ -131,20 +131,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; From 9f00a1659be8e12a6031f6d4a2bf121b7616a417 Mon Sep 17 00:00:00 2001 From: Lulu Date: Thu, 19 Jan 2023 09:07:53 +0100 Subject: [PATCH 09/25] Added a callback when a sound is stopped --- source/alsound.pas | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/source/alsound.pas b/source/alsound.pas index 8ef4b08..8f6e5ff 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -330,6 +330,7 @@ TALSErrorHandling = class end; + TALSNotifyEvent = procedure(Sender: TALSSound) of object; TALSOnCustomDSP = procedure(Sender: TALSSound; const aBuffer: TALSPlaybackBuffer) of object; @@ -403,6 +404,10 @@ TALSSound = class( TALSErrorHandling ) private FOnCustomDSP: TALSOnCustomDSP; procedure SetOnCustomDSP(AValue: TALSOnCustomDSP); + private + FPreviousState: TALSState; + FOnStopped: TALSNotifyEvent; + procedure SetOnStopped(AValue: TALSNotifyEvent); private function GetTimePosition: single; virtual; procedure SetTimePosition(AValue: single); virtual; @@ -542,6 +547,8 @@ TALSSound = class( TALSErrorHandling ) // 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; // general purpose @@ -760,7 +767,9 @@ TALSPlaybackContext = class(TALSErrorHandling) FCriticalSection: TRTLCriticalSection; FThread: TALSThread; FThreadIsStarted: boolean; + FSoundToProcess: TALSSound; procedure DoUpdate(const aElapsedTime: single); + procedure DoSoundOnStopped; procedure EnterCS; procedure LeaveCS; protected @@ -4444,7 +4453,6 @@ function TALSPlaybackContext.CreateWhiteNoise(aDuration: single; procedure TALSPlaybackContext.DoUpdate(const aElapsedTime: single); var i: integer; - s: TALSSound; begin EnterCriticalSection(FCriticalSection); FThreadIsStarted:=True; @@ -4467,11 +4475,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 @@ -4489,6 +4497,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); @@ -5137,6 +5151,16 @@ procedure TALSSound.SetOnCustomDSP(AValue: TALSOnCustomDSP); end; end; +procedure TALSSound.SetOnStopped(AValue: TALSNotifyEvent); +begin + EnterCS; + try + FOnStopped := AValue; + finally + LeaveCS; + end; +end; + function TALSSound.GetFormatForAL(aChannelCount: integer; aContextUseFloat, aWantBFormatAmbisonic: boolean): DWord; begin @@ -5664,7 +5688,10 @@ procedure TALSSound.LeaveCS; procedure TALSSound.Update(const aElapsedTime: single); var v: single; + flagDoOnStopped: boolean; begin + flagDoOnStopped := False; + EnterCriticalSection(FCriticalSection); try v := Volume.Value; @@ -5689,9 +5716,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; From 56010a3675809676505877920e1b12ded5484d82 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sat, 21 Jan 2023 11:27:52 +0100 Subject: [PATCH 10/25] Clarified some comments --- source/alsound.pas | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/alsound.pas b/source/alsound.pas index 8f6e5ff..3bcbc5b 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -992,19 +992,19 @@ TALSLoopbackContext = class(TALSPlaybackContext) 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, + // Callback OnProgress must be defined and 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; - // Cancel the current mixing process. If the mix is saved to an output file, - // the file is deleted. + // Cancel a mixing process previously started with StartMixing. 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; - // This callback is fired after a call to StartMixing, each time the buffer - // is filled with audio. It allows your application to: + // 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. From f64189d5577354df35b56203d757b809ead2ebe1 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sat, 21 Jan 2023 11:56:56 +0100 Subject: [PATCH 11/25] Refactored some code --- source/alsound.pas | 71 +++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/source/alsound.pas b/source/alsound.pas index 3bcbc5b..38c2fdd 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -971,6 +971,9 @@ 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. @@ -1443,6 +1446,44 @@ procedure TALSLoopbackContext.RenderAudioToBuffer; 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); + + // operation was canceled ? + if FCancelMixing then + DeleteFile(FFilename); + + 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; @@ -1543,7 +1584,6 @@ function TALSLoopbackContext.PrepareSavingToFile(const aFilename: string; procedure TALSLoopbackContext.StartMixing; var posTime: double; - written: sf_count_t; done, flagSaveToFile: boolean; begin if Error or @@ -1551,7 +1591,7 @@ procedure TALSLoopbackContext.StartMixing; exit; if FOnProgress = NIL then - Raise Exception.Create('TALSLoopbackContext.StartMixing - Callback OnProgress must be defined'); + DoExceptionNoCallback; ResetMixingError; @@ -1574,18 +1614,8 @@ procedure TALSLoopbackContext.StartMixing; // 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; + if flagSaveToFile then + SaveBufferToFile; {$ifdef LCL} Application.ProcessMessages; @@ -1596,18 +1626,7 @@ procedure TALSLoopbackContext.StartMixing; 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; - end; + CloseFile; FFrameBuffer.FreeMemory; FIsMixing := False; From 76909062aba60d91ce4502b3f7d0b1fa4a8abc1f Mon Sep 17 00:00:00 2001 From: Lulu Date: Sat, 21 Jan 2023 13:52:24 +0100 Subject: [PATCH 12/25] Refactored some code --- source/alsound.pas | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/source/alsound.pas b/source/alsound.pas index 38c2fdd..026d29c 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -1583,7 +1583,7 @@ function TALSLoopbackContext.PrepareSavingToFile(const aFilename: string; procedure TALSLoopbackContext.StartMixing; var - posTime: double; + posTime, deltaTime: double; done, flagSaveToFile: boolean; begin if Error or @@ -1611,8 +1611,11 @@ procedure TALSLoopbackContext.StartMixing; // Render some audio RenderAudioToBuffer; + deltaTime := FFrameBuffer.FrameCount/FSampleRate; + posTime := posTime + deltaTime; + // Call the callback - FOnProgress(Self, posTime+FTimeSlice, FFrameBuffer, flagSaveToFile, done); + FOnProgress(Self, posTime, FFrameBuffer, flagSaveToFile, done); if flagSaveToFile then SaveBufferToFile; @@ -1621,9 +1624,7 @@ procedure TALSLoopbackContext.StartMixing; Application.ProcessMessages; {$endif} - Update(FTimeSlice); - - posTime := posTime + FTimeSlice; + Update(deltaTime); end; CloseFile; From ea9a54e228fb66c48e4f4ba664bc32bf60415f29 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sun, 22 Jan 2023 15:48:34 +0100 Subject: [PATCH 13/25] Refactored the mixing process in TALSLoopbackContext We can now specify the amount of time to mix. --- source/alsound.pas | 110 ++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/source/alsound.pas b/source/alsound.pas index 026d29c..1a02262 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -944,8 +944,7 @@ TALSLoopbackContext = class; TALSProgressEvent = procedure(Sender: TALSLoopbackContext; aTimePos: double; const aFrameBuffer: TALSLoopbackFrameBuffer; - var SaveBufferToFile: boolean; - var Done: boolean) of object; + var SaveBufferToFile: boolean) of object; { TALSLoopbackContext } @@ -958,10 +957,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; @@ -994,17 +993,20 @@ 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 must be defined and 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 a mixing process previously started with StartMixing. 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); + + // 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: @@ -1471,10 +1473,6 @@ procedure TALSLoopbackContext.CloseFile; sf_write_sync(FFile); sf_close(FFile); - // operation was canceled ? - if FCancelMixing then - DeleteFile(FFilename); - FFile := NIL; end; end; @@ -1575,67 +1573,68 @@ function TALSLoopbackContext.PrepareSavingToFile(const aFilename: string; FFileInfo. Format := aFileFormat; FFileInfo.Channels := FFrameBuffer.ChannelCount; FFile := ALSOpenAudioFile(aFilename, SFM_WRITE, @FFileInfo); -{ if FFile = NIL then - SetMixingError(als_CanNotCreateTargetMixFile); } Result := FFile <> NIL; end; -procedure TALSLoopbackContext.StartMixing; -var - posTime, deltaTime: double; - done, flagSaveToFile: boolean; +procedure TALSLoopbackContext.BeginOfMix; begin - if Error or - FIsMixing then - exit; - if FOnProgress = NIL then - DoExceptionNoCallback; + DoExceptionNoCallback + else begin + FIsMixing := True; + FMixTime := 0.0; + end; +end; - ResetMixingError; +procedure TALSLoopbackContext.Mix(aDuration: single); +var + deltaTime: double; + flagSaveToFile: boolean; +begin + if Error or MixingError then + exit; - FIsMixing := True; - FCancelMixing := False; + flagSaveToFile := (FFile <> NIL); - // reserves buffer memory - FFrameBuffer.FrameCapacity := Round(FSampleRate*FTimeSlice); + while (aDuration > 0) and not MixingError do + begin + // we need to mix slice of time defined by user + deltaTime := Min(aDuration, FTimeSlice); + aDuration := aDuration - deltaTime; - posTime := 0.0; - done := False; - flagSaveToFile := True; + // reserves buffer memory + if FFrameBuffer.FrameCapacity <> Round(FSampleRate*deltaTime) then + FFrameBuffer.FrameCapacity := Round(FSampleRate*deltaTime); - while not MixingError and - not FCancelMixing and not done do - begin - // Render some audio + // Render audio RenderAudioToBuffer; + // update the context deltaTime := FFrameBuffer.FrameCount/FSampleRate; - posTime := posTime + deltaTime; + Update(deltaTime); + FMixTime := FMixTime + deltaTime; - // Call the callback - FOnProgress(Self, posTime, FFrameBuffer, flagSaveToFile, done); + // Callback + FOnProgress(Self, FMixTime, FFrameBuffer, flagSaveToFile); if flagSaveToFile then SaveBufferToFile; {$ifdef LCL} - Application.ProcessMessages; + if aDuration > 0 then + Application.ProcessMessages; {$endif} - - Update(deltaTime); end; - - CloseFile; - - FFrameBuffer.FreeMemory; - FIsMixing := False; end; -procedure TALSLoopbackContext.CancelMix; +procedure TALSLoopbackContext.EndOfMix; begin - FCancelMixing := True; + if FIsMixing then begin + CloseFile; + FFrameBuffer.FreeMemory; + FIsMixing := False; + end; end; { TALSLoopbackDeviceItem } @@ -3775,6 +3774,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; From ed6a198caed0ebca67686e3a7b4faba55362fcb5 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sun, 22 Jan 2023 15:48:52 +0100 Subject: [PATCH 14/25] updated example --- example/Loopback/loopback.lps | 100 +++++++++++++++++----------------- example/Loopback/unit1.pas | 44 ++++++++------- 2 files changed, 75 insertions(+), 69 deletions(-) diff --git a/example/Loopback/loopback.lps b/example/Loopback/loopback.lps index bec3982..c1cfb51 100644 --- a/example/Loopback/loopback.lps +++ b/example/Loopback/loopback.lps @@ -21,8 +21,8 @@ - - + + @@ -312,8 +312,8 @@ - - + + @@ -427,124 +427,124 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - + diff --git a/example/Loopback/unit1.pas b/example/Loopback/unit1.pas index 8741e86..8639bfa 100644 --- a/example/Loopback/unit1.pas +++ b/example/Loopback/unit1.pas @@ -86,15 +86,16 @@ TForm1 = class(TForm) FLoopbackContext: TALSLoopbackContext; // tracks instance FTracks: array[0..2] of TTrack; + FMixingTime: double; 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: boolean); public end; @@ -126,8 +127,6 @@ procedure TForm1.BitBtn1Click(Sender: TObject); procedure TForm1.BCancelClick(Sender: TObject); begin // User want to cancel the mix - FLoopbackContext.CancelMix; - BCancel.Tag := 1; end; @@ -259,7 +258,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 +288,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,18 +302,26 @@ 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; + // 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 + (BCancel.Tag <> 0); // 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. @@ -324,7 +331,7 @@ procedure TForm1.BMixToFileClick(Sender: TObject); Showmessage(FLoopbackContext.MixingStrError) else ShowMessage('Mixdown saved to' + lineending + - outputFilename + lineending + 'SUCCESS'); + outputFilename + lineending + 'WITH SUCCESS'); end else BCancel.Tag := 0; @@ -337,7 +344,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 +412,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 +433,10 @@ procedure TForm1.EnableMix(aState: boolean); // procedure TForm1.ProcessLoopbackContextOnProgress(Sender: TALSLoopbackContext; aTimePos: double; const aFrameBuffer: TALSLoopbackFrameBuffer; - var SaveBufferToFile: boolean; var Done: boolean); + var SaveBufferToFile: boolean); begin + FMixingTime := aTimePos; + // update the progress bar according to the current mixing time position ProgressBar1.Position := Round(ProgressBar1.Max*aTimePos/FloatSpinEdit2.Value); @@ -442,9 +451,6 @@ procedure TForm1.ProcessLoopbackContextOnProgress(Sender: TALSLoopbackContext; // position is inside the interval the user entered. 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; end; end. From 71a2800e30e67e09204a5d1c6ace7409bfbed384 Mon Sep 17 00:00:00 2001 From: Lulu Date: Mon, 23 Jan 2023 16:25:50 +0100 Subject: [PATCH 15/25] Added a mean to cancel a mixing --- source/alsound.pas | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/source/alsound.pas b/source/alsound.pas index 1a02262..99cf73a 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -944,7 +944,8 @@ TALSLoopbackContext = class; TALSProgressEvent = procedure(Sender: TALSLoopbackContext; aTimePos: double; const aFrameBuffer: TALSLoopbackFrameBuffer; - var SaveBufferToFile: boolean) of object; + var SaveBufferToFile: boolean; + var Cancel: boolean) of object; { TALSLoopbackContext } @@ -1590,14 +1591,15 @@ procedure TALSLoopbackContext.BeginOfMix; procedure TALSLoopbackContext.Mix(aDuration: single); var deltaTime: double; - flagSaveToFile: boolean; + flagSaveToFile, flagCancel: boolean; begin if Error or MixingError then exit; flagSaveToFile := (FFile <> NIL); + flagCancel := False; - while (aDuration > 0) and not MixingError do + while (aDuration > 0.0005) and not MixingError and not flagCancel do begin // we need to mix slice of time defined by user deltaTime := Min(aDuration, FTimeSlice); @@ -1616,15 +1618,10 @@ procedure TALSLoopbackContext.Mix(aDuration: single); FMixTime := FMixTime + deltaTime; // Callback - FOnProgress(Self, FMixTime, FFrameBuffer, flagSaveToFile); + FOnProgress(Self, FMixTime, FFrameBuffer, flagSaveToFile, flagCancel); if flagSaveToFile then SaveBufferToFile; - - {$ifdef LCL} - if aDuration > 0 then - Application.ProcessMessages; - {$endif} end; end; From b47206f8c8e6234f085ce75e92d5e82196b3cfe3 Mon Sep 17 00:00:00 2001 From: Lulu Date: Mon, 23 Jan 2023 16:26:01 +0100 Subject: [PATCH 16/25] Updated examples --- example/Loopback/loopback.lps | 106 +++++++++--------- example/Loopback/unit1.pas | 18 +-- example/console_alplay/console_alplay.lpr | 2 +- example/console_alplay/console_alplay.lps | 28 ++++- .../console_fileformat/console_fileformat.lps | 2 +- .../console_openal_info.lps | 2 +- example/effect_parameter/EffectParameter.lpi | 1 + example/effect_parameter/EffectParameter.lps | 2 +- example/effects/effects.lps | 70 ++++++------ example/effects/unit1.pas | 4 +- example/simplerecorder/SimpleRecorder.lps | 76 ++++++------- example/simplerecorder/unit2.lfm | 2 +- example/simplerecorder/unit2.pas | 3 +- 13 files changed, 168 insertions(+), 148 deletions(-) diff --git a/example/Loopback/loopback.lps b/example/Loopback/loopback.lps index c1cfb51..957187c 100644 --- a/example/Loopback/loopback.lps +++ b/example/Loopback/loopback.lps @@ -21,8 +21,8 @@ - - + + @@ -427,124 +427,124 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - + - - + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - - + + - - + + - + diff --git a/example/Loopback/unit1.pas b/example/Loopback/unit1.pas index 8639bfa..c0714bb 100644 --- a/example/Loopback/unit1.pas +++ b/example/Loopback/unit1.pas @@ -87,6 +87,7 @@ TForm1 = class(TForm) // tracks instance FTracks: array[0..2] of TTrack; FMixingTime: double; + FCanceled: boolean; procedure InitTracks; function GetSampleRate: integer; function GetChannel: TALSLoopbackChannel; @@ -95,7 +96,7 @@ TForm1 = class(TForm) private procedure ProcessLoopbackContextOnProgress(Sender: TALSLoopbackContext; aTimePos: double; const aFrameBuffer: TALSLoopbackFrameBuffer; - var SaveBufferToFile: boolean); + var SaveBufferToFile, Cancel: boolean); public end; @@ -127,7 +128,7 @@ procedure TForm1.BitBtn1Click(Sender: TObject); procedure TForm1.BCancelClick(Sender: TObject); begin // User want to cancel the mix - BCancel.Tag := 1; + FCanceled := True; end; procedure TForm1.ComboBox2Select(Sender: TObject); @@ -311,6 +312,8 @@ procedure TForm1.BMixToFileClick(Sender: TObject); // filled with audio FLoopbackContext.OnProgress := @ProcessLoopbackContextOnProgress; + FCanceled := False; + // We have to call this method before render audio. FLoopbackContext.BeginOfMix; @@ -318,13 +321,13 @@ procedure TForm1.BMixToFileClick(Sender: TObject); // 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 - (BCancel.Tag <> 0); // user click cancel button + 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 @@ -332,8 +335,7 @@ procedure TForm1.BMixToFileClick(Sender: TObject); else ShowMessage('Mixdown saved to' + lineending + outputFilename + lineending + 'WITH SUCCESS'); - end - else BCancel.Tag := 0; + end; // Free loopback context (and loopback device) FreeAndNil(FLoopbackContext); @@ -433,7 +435,7 @@ procedure TForm1.EnableMixGUI(aState: boolean); // procedure TForm1.ProcessLoopbackContextOnProgress(Sender: TALSLoopbackContext; aTimePos: double; const aFrameBuffer: TALSLoopbackFrameBuffer; - var SaveBufferToFile: boolean); + var SaveBufferToFile, Cancel: boolean); begin FMixingTime := aTimePos; @@ -451,6 +453,8 @@ procedure TForm1.ProcessLoopbackContextOnProgress(Sender: TALSLoopbackContext; // position is inside the interval the user entered. SaveBufferToFile := (aTimePos >= FloatSpinEdit1.Value) and (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..42a5b75 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/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; From 69d148faa041e6693d05f4b57cfd35b584210c4a Mon Sep 17 00:00:00 2001 From: Lulu Date: Thu, 2 Feb 2023 17:04:23 +0100 Subject: [PATCH 17/25] Rewrited code to better manage OpenAL-Soft extension --- source/alext.inc | 36 ++++-- source/als_deviceitem.inc | 231 +++++++++++++++++++++++++++++++++++++ source/als_error.inc | 1 - source/alsound.pas | 232 +++++++++++--------------------------- source/openalsoft.pas | 115 +++++++++---------- 5 files changed, 376 insertions(+), 239 deletions(-) create mode 100644 source/als_deviceitem.inc 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_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/alsound.pas b/source/alsound.pas index 99cf73a..275d1a9 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -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 } @@ -775,8 +776,10 @@ TALSPlaybackContext = class(TALSErrorHandling) protected FExecutingConstructor: boolean; private + FHaveExt_ALC_SOFT_HRTF: boolean; FList: TFPList; FPlaylist: TALSPlaylist; + FParentDeviceItem: PALSDeviceItem; FParentDevice: PALCdevice; FContext: PALCcontext; FDistanceModel: TALSDistanceModel; @@ -786,19 +789,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, @@ -809,6 +807,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; @@ -830,7 +830,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. @@ -916,7 +916,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. @@ -926,7 +926,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 @@ -977,7 +977,7 @@ TALSLoopbackContext = class(TALSPlaybackContext) 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; @@ -1144,32 +1144,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; @@ -1261,7 +1235,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; @@ -1377,6 +1351,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; @@ -1442,7 +1417,8 @@ 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; @@ -1502,16 +1478,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 ); @@ -1529,7 +1506,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; @@ -1634,96 +1611,6 @@ procedure TALSLoopbackContext.EndOfMix; end; end; -{ TALSLoopbackDeviceItem } - -procedure TALSLoopbackDeviceItem.InitDefault; -begin - Name := ''; - Handle := nil; - OpenedCount := 0; -end; - -procedure TALSLoopbackDeviceItem.Open; -begin - if not ALSManager.Error then - 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; - -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 - Name := ''; - Handle := nil; - OpenedCount := 0; -end; - -procedure TALSPlaybackDeviceItem.Open; -begin - if not ALSManager.Error then - begin - if Handle = NIL then - Handle := alcOpenDevice(PChar(Name)); - 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; - - { TALSAudioFileFormat } function TALSAudioFileFormat.SubFormatCount: integer; @@ -2401,10 +2288,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 @@ -2683,7 +2573,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; @@ -2700,18 +2590,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; @@ -3463,7 +3349,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 ); @@ -3630,7 +3516,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; @@ -4565,7 +4451,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 @@ -4634,7 +4520,7 @@ function TALSPlaybackContext.ChangeAttributes(const aAttribs: TALSContextAttribu begin LockContext( FContext ); try - Result := alcResetDeviceSOFT( FParentDevice, @aAttribs ); + Result := FParentDeviceItem^.alcResetDeviceSOFT( FParentDevice, @aAttribs ); finally UnlockContext; end; @@ -4754,10 +4640,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; @@ -4773,7 +4669,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; @@ -4869,31 +4765,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 @@ -4920,7 +4816,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(); @@ -5021,14 +4917,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); @@ -5131,7 +5029,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 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; From 2e40278edc88539cdbe3d0e85fbeaba9e36682a5 Mon Sep 17 00:00:00 2001 From: Lulu Date: Thu, 2 Feb 2023 17:04:45 +0100 Subject: [PATCH 18/25] Updated examples --- example/effects/effects.lps | 2 +- example/multi_context/MultiContext.lps | 76 +++++++++++++------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/example/effects/effects.lps b/example/effects/effects.lps index 42a5b75..20d97a1 100644 --- a/example/effects/effects.lps +++ b/example/effects/effects.lps @@ -22,7 +22,7 @@ - + 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 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - + - - + + - + From 338b9a3d42996acbb656255bdc3c74f2ea5c89c4 Mon Sep 17 00:00:00 2001 From: Lulu Date: Mon, 6 Feb 2023 14:52:00 +0100 Subject: [PATCH 19/25] Fix a bug in thread in TALSStreamBufferSound --- source/alsound.pas | 98 +++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/source/alsound.pas b/source/alsound.pas index 275d1a9..2cceb4d 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -3805,63 +3805,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 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 + 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; From a31d3b05d27492258c2c911b5636939db05d2ceb Mon Sep 17 00:00:00 2001 From: Lulu Date: Tue, 7 Feb 2023 17:47:09 +0100 Subject: [PATCH 20/25] Added user data to custom dsp callback --- source/alsound.pas | 83 ++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/source/alsound.pas b/source/alsound.pas index 2cceb4d..294fbd4 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -333,7 +333,8 @@ TALSErrorHandling = class TALSNotifyEvent = procedure(Sender: TALSSound) of object; TALSOnCustomDSP = procedure(Sender: TALSSound; - const aBuffer: TALSPlaybackBuffer) of object; + const aBuffer: TALSPlaybackBuffer; + aUserData: Pointer) of object; { TALSSound } @@ -404,7 +405,7 @@ TALSSound = class( TALSErrorHandling ) procedure FreeParameters; private FOnCustomDSP: TALSOnCustomDSP; - procedure SetOnCustomDSP(AValue: TALSOnCustomDSP); + FOnCustomDSPUserData: pointer; private FPreviousState: TALSState; FOnStopped: TALSNotifyEvent; @@ -483,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 @@ -540,14 +550,6 @@ 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 @@ -573,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; @@ -615,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; @@ -838,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 } @@ -854,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); @@ -2828,7 +2837,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; @@ -3772,7 +3781,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, @@ -3849,7 +3858,7 @@ procedure TALSStreamBufferSound.Update(const aElapsedTime: single); begin // callback OnNewBuffer if FOnCustomDSP <> NIL then - FOnCustomDSP(Self, FBuffers[bufferIndex]); + FOnCustomDSP(Self, FBuffers[bufferIndex], FOnCustomDSPUserData); // refill the openAL buffer with... alBufferData(bufid, ALenum(FFormatForAL), FBuffers[bufferIndex].Data, ALsizei(readCount * FFrameSize), ALsizei(Fsfinfo.SampleRate)); @@ -3888,7 +3897,7 @@ procedure TALSStreamBufferSound.InternalRewind; constructor TALSStreamBufferSound.CreateFromFile(aParent: TALSPlaybackContext; const aFilename: string; aEnableMonitor: boolean; - aOnCustomDSP: TALSOnCustomDSP); + aOnCustomDSP: TALSOnCustomDSP; aCustomDSPUserData: Pointer); var fileopened: boolean; begin @@ -3899,6 +3908,7 @@ constructor TALSStreamBufferSound.CreateFromFile(aParent: TALSPlaybackContext; FFilename := aFilename; FMonitoringEnabled := aEnableMonitor; FOnCustomDSP := aOnCustomDSP; + FOnCustomDSPUserData := aCustomDSPUserData; FDoReadFromStream := @DoReadStreamFromFile; @@ -4074,7 +4084,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; @@ -4088,6 +4099,7 @@ constructor TALSSingleStaticBufferSound.CreateFromFile(aParent: TALSPlaybackCont FFilename := aFilename; FMonitoringEnabled := aEnableMonitor; FOnCustomDSP := aOnCustomDSP; + FOnCustomDSPUserData := aCustomDSPUserData; if not Error then begin @@ -4130,7 +4142,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; @@ -4170,13 +4182,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 @@ -4216,7 +4229,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); @@ -4333,12 +4346,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); @@ -4347,13 +4361,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); @@ -4951,14 +4966,15 @@ constructor TALSPlaybackContext.Create(aDevice: PALSPlaybackDeviceItem; 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); @@ -5064,11 +5080,12 @@ 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; From 757b45670f98bc2c3cb23850f579c5c0da140064 Mon Sep 17 00:00:00 2001 From: Lulu Date: Fri, 10 Feb 2023 17:30:27 +0100 Subject: [PATCH 21/25] Added dsp_AmplifySample_xxxxx routine for small int and float --- source/als_dsp_utils.pas | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/source/als_dsp_utils.pas b/source/als_dsp_utils.pas index 8d545ac..a560647 100644 --- a/source/als_dsp_utils.pas +++ b/source/als_dsp_utils.pas @@ -71,6 +71,8 @@ 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; @@ -379,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. From d8b89c7c9fd874d7887c2280714843aad87c2436 Mon Sep 17 00:00:00 2001 From: Lulu Date: Fri, 10 Feb 2023 17:32:29 +0100 Subject: [PATCH 22/25] modified some comment --- source/alsound.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/alsound.pas b/source/alsound.pas index 294fbd4..b328e36 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -3856,7 +3856,7 @@ procedure TALSStreamBufferSound.Update(const aElapsedTime: single); if readCount > 0 then begin - // callback OnNewBuffer + // callback custom DSP if FOnCustomDSP <> NIL then FOnCustomDSP(Self, FBuffers[bufferIndex], FOnCustomDSPUserData); // refill the openAL buffer with... From d10e8ec3476f5c4c854eafad53c30d16c619e192 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sun, 12 Feb 2023 09:18:57 +0100 Subject: [PATCH 23/25] Increased the playback context thread priority --- source/alsound.pas | 1 + 1 file changed, 1 insertion(+) diff --git a/source/alsound.pas b/source/alsound.pas index b328e36..bdb9808 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -4959,6 +4959,7 @@ constructor TALSPlaybackContext.Create(aDevice: PALSPlaybackDeviceItem; 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); From 6b3b12b6bf049fdc304b612c3f7ac30b003c8b1e Mon Sep 17 00:00:00 2001 From: Lulu Date: Sun, 12 Feb 2023 10:07:05 +0100 Subject: [PATCH 24/25] Fixed a bug in Loopback context with float sample type --- source/alsound.pas | 1 + 1 file changed, 1 insertion(+) diff --git a/source/alsound.pas b/source/alsound.pas index bdb9808..c35a6a5 100644 --- a/source/alsound.pas +++ b/source/alsound.pas @@ -2663,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, From f75f3002671e25f2b6b96acc55fe75c261e84792 Mon Sep 17 00:00:00 2001 From: Lulu Date: Wed, 15 Feb 2023 14:22:13 +0100 Subject: [PATCH 25/25] Update version to 2.0.0 --- source/alsound.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/alsound.pas b/source/alsound.pas index c35a6a5..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