From ce4b2cba82398577edf04cce8abccf95bd93ea32 Mon Sep 17 00:00:00 2001 From: tobigun Date: Fri, 15 Aug 2008 18:52:50 +0000 Subject: Race-Conditions fixed in recording stuff. git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1267 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UDraw.pas | 10 ++- Game/Code/Classes/ULog.pas | 4 +- Game/Code/Classes/URecord.pas | 143 ++++++++++++++++++++++++++---------------- 3 files changed, 98 insertions(+), 59 deletions(-) (limited to 'Game/Code') diff --git a/Game/Code/Classes/UDraw.pas b/Game/Code/Classes/UDraw.pas index e405ec07..ff0920f5 100644 --- a/Game/Code/Classes/UDraw.pas +++ b/Game/Code/Classes/UDraw.pas @@ -173,13 +173,17 @@ begin; MaxX := W-1; MaxY := (H-1) / 2; + Sound.LockAnalysisBuffer(); + glBegin(GL_LINE_STRIP); - for SampleIndex := 0 to High(Sound.BufferArray) do + for SampleIndex := 0 to High(Sound.AnalysisBuffer) do begin - glVertex2f(X + MaxX * SampleIndex/High(Sound.BufferArray), - Y + MaxY * (1 - Sound.BufferArray[SampleIndex]/-Low(Smallint))); + glVertex2f(X + MaxX * SampleIndex/High(Sound.AnalysisBuffer), + Y + MaxY * (1 - Sound.AnalysisBuffer[SampleIndex]/-Low(Smallint))); end; glEnd; + + Sound.UnlockAnalysisBuffer(); end; diff --git a/Game/Code/Classes/ULog.pas b/Game/Code/Classes/ULog.pas index 8394d73e..a9a2f3c4 100644 --- a/Game/Code/Classes/ULog.pas +++ b/Game/Code/Classes/ULog.pas @@ -388,8 +388,8 @@ begin FS := TFileStream.Create(FileName, fmCreate); - AudioInputProcessor.Sound[SoundNr].BufferLong.Seek(0, soBeginning); - FS.CopyFrom(AudioInputProcessor.Sound[SoundNr].BufferLong, AudioInputProcessor.Sound[SoundNr].BufferLong.Size); + AudioInputProcessor.Sound[SoundNr].LogBuffer.Seek(0, soBeginning); + FS.CopyFrom(AudioInputProcessor.Sound[SoundNr].LogBuffer, AudioInputProcessor.Sound[SoundNr].LogBuffer.Size); FS.Free; end; diff --git a/Game/Code/Classes/URecord.pas b/Game/Code/Classes/URecord.pas index fdd8bb9c..8a537dc9 100644 --- a/Game/Code/Classes/URecord.pas +++ b/Game/Code/Classes/URecord.pas @@ -11,6 +11,7 @@ interface uses Classes, Math, SysUtils, + sdl, UCommon, UMusic, UIni; @@ -23,13 +24,23 @@ type TCaptureBuffer = class private VoiceStream: TAudioVoiceStream; // stream for voice passthrough + AnalysisBufferLock: PSDL_Mutex; function GetToneString: string; // converts a tone to its string represenatation; + + procedure BoostBuffer(Buffer: PChar; Size: Cardinal); + procedure ProcessNewBuffer(Buffer: PChar; BufferSize: integer); + + // we call it to analyze sound by checking Autocorrelation + procedure AnalyzeByAutocorrelation; + // use this to check one frequency by Autocorrelation + function AnalyzeAutocorrelationFreq(Freq: real): real; public - BufferArray: array[0..4095] of smallint; // newest 4096 samples - BufferLong: TMemoryStream; // full buffer + AnalysisBuffer: array[0..4095] of smallint; // newest 4096 samples AnalysisBufferSize: integer; // number of samples of BufferArray to analyze + LogBuffer: TMemoryStream; // full buffer + AudioFormat: TAudioFormatInfo; // pitch detection @@ -44,17 +55,12 @@ type procedure Clear; - procedure BoostBuffer(Buffer: PChar; Size: Cardinal); - procedure ProcessNewBuffer(Buffer: PChar; BufferSize: integer); - // use to analyze sound from buffers to get new pitch procedure AnalyzeBuffer; - // we call it to analyze sound by checking Autocorrelation - procedure AnalyzeByAutocorrelation; - // use this to check one frequency by Autocorrelation - function AnalyzeAutocorrelationFreq(Freq: real): real; - function MaxSampleVolume: Single; + procedure LockAnalysisBuffer(); {$IFDEF HasInline}inline;{$ENDIF} + procedure UnlockAnalysisBuffer(); {$IFDEF HasInline}inline;{$ENDIF} + function MaxSampleVolume: Single; property ToneString: string READ GetToneString; end; @@ -137,11 +143,6 @@ var singleton_AudioInputProcessor : TAudioInputProcessor = nil; -// FIXME: -// Race-Conditions between Callback-thread and main-thread on BufferArray. -// Use mutexes to solve this problem. - - { Global } function AudioInputProcessor(): TAudioInputProcessor; @@ -208,23 +209,37 @@ end; constructor TCaptureBuffer.Create; begin inherited; - BufferLong := TMemoryStream.Create; - AnalysisBufferSize := Min(4*1024, Length(BufferArray)); + LogBuffer := TMemoryStream.Create; + AnalysisBufferLock := SDL_CreateMutex(); + AnalysisBufferSize := Length(AnalysisBuffer); end; destructor TCaptureBuffer.Destroy; begin - FreeAndNil(BufferLong); + FreeAndNil(LogBuffer); FreeAndNil(VoiceStream); FreeAndNil(AudioFormat); + SDL_DestroyMutex(AnalysisBufferLock); inherited; end; +procedure TCaptureBuffer.LockAnalysisBuffer(); +begin + SDL_mutexP(AnalysisBufferLock); +end; + +procedure TCaptureBuffer.UnlockAnalysisBuffer(); +begin + SDL_mutexV(AnalysisBufferLock); +end; + procedure TCaptureBuffer.Clear; begin - if assigned(BufferLong) then - BufferLong.Clear; - FillChar(BufferArray[0], Length(BufferArray) * SizeOf(SmallInt), 0); + if assigned(LogBuffer) then + LogBuffer.Clear; + LockAnalysisBuffer(); + FillChar(AnalysisBuffer[0], Length(AnalysisBuffer) * SizeOf(SmallInt), 0); + UnlockAnalysisBuffer(); end; procedure TCaptureBuffer.ProcessNewBuffer(Buffer: PChar; BufferSize: integer); @@ -251,20 +266,29 @@ begin SampleCount := BufferSize div SizeOf(SmallInt); // check if we have more new samples than we can store - if (SampleCount > Length(BufferArray)) then + if (SampleCount > Length(AnalysisBuffer)) then begin // discard the oldest of the new samples - BufferOffset := (SampleCount - Length(BufferArray)) * SizeOf(SmallInt); - SampleCount := Length(BufferArray); + BufferOffset := (SampleCount - Length(AnalysisBuffer)) * SizeOf(SmallInt); + SampleCount := Length(AnalysisBuffer); end; - // move old samples to the beginning of the array (if necessary) - for i := 0 to High(BufferArray)-SampleCount do - BufferArray[i] := BufferArray[i+SampleCount]; - // copy samples to analysis buffer - Move(Buffer[BufferOffset], BufferArray[Length(BufferArray)-SampleCount], - SampleCount * SizeOf(SmallInt)); + LockAnalysisBuffer(); + try + + // move old samples to the beginning of the array (if necessary) + for i := 0 to High(AnalysisBuffer)-SampleCount do + AnalysisBuffer[i] := AnalysisBuffer[i+SampleCount]; + + // copy new samples to analysis buffer + Move(Buffer[BufferOffset], AnalysisBuffer[Length(AnalysisBuffer)-SampleCount], + SampleCount * SizeOf(SmallInt)); + + finally + UnlockAnalysisBuffer(); + end; + // save capture-data to BufferLong if enabled if (Ini.SavePlayback = 1) then @@ -273,7 +297,7 @@ begin // For an in-game replay-mode we need to compress data so we do not // waste that much memory. Maybe ogg-vorbis with voice-preset in fast-mode? // Or we could use a faster but not that efficient lossless compression. - BufferLong.WriteBuffer(Buffer, BufferSize); + LogBuffer.WriteBuffer(Buffer, BufferSize); end; end; @@ -288,23 +312,30 @@ begin ToneAbs := -1; Tone := -1; - // find maximum volume of first 1024 samples - MaxVolume := 0; - for SampleIndex := 0 to 1023 do - begin - Volume := Abs(BufferArray[SampleIndex]) / -Low(Smallint); - if Volume > MaxVolume then - MaxVolume := Volume; - end; + LockAnalysisBuffer(); + try - Threshold := IThresholdVals[Ini.ThresholdIndex]; - - // check if signal has an acceptable volume (ignore background-noise) - if MaxVolume >= Threshold then - begin - // analyse the current voice pitch - AnalyzeByAutocorrelation; - ToneValid := true; + // find maximum volume of first 1024 samples + MaxVolume := 0; + for SampleIndex := 0 to 1023 do + begin + Volume := Abs(AnalysisBuffer[SampleIndex]) / -Low(Smallint); + if Volume > MaxVolume then + MaxVolume := Volume; + end; + + Threshold := IThresholdVals[Ini.ThresholdIndex]; + + // check if signal has an acceptable volume (ignore background-noise) + if MaxVolume >= Threshold then + begin + // analyse the current voice pitch + AnalyzeByAutocorrelation; + ToneValid := true; + end; + + finally + UnlockAnalysisBuffer(); end; end; @@ -363,7 +394,7 @@ begin while (CorrelatingSampleIndex < AnalysisBufferSize) do begin // calc distance (correlation: 1-dist) to corresponding sample in next period - Dist := Abs(BufferArray[SampleIndex] - BufferArray[CorrelatingSampleIndex]) / + Dist := Abs(AnalysisBuffer[SampleIndex] - AnalysisBuffer[CorrelatingSampleIndex]) / High(Word); AccumDist := AccumDist + Dist; Inc(SampleIndex); @@ -379,12 +410,16 @@ var lSampleIndex: Integer; lMaxVol : Longint; begin; - // FIXME: lock buffer to avoid race-conditions - lMaxVol := 0; - for lSampleIndex := 0 to High(BufferArray) do - begin - if Abs(BufferArray[lSampleIndex]) > lMaxVol then - lMaxVol := Abs(BufferArray[lSampleIndex]); + LockAnalysisBuffer(); + try + lMaxVol := 0; + for lSampleIndex := 0 to High(AnalysisBuffer) do + begin + if Abs(AnalysisBuffer[lSampleIndex]) > lMaxVol then + lMaxVol := Abs(AnalysisBuffer[lSampleIndex]); + end; + finally + UnlockAnalysisBuffer(); end; result := lMaxVol / -Low(Smallint); -- cgit v1.2.3