diff options
Diffstat (limited to 'Game/Code/Classes')
-rw-r--r-- | Game/Code/Classes/UAudioInput_Portaudio.pas | 7 | ||||
-rw-r--r-- | Game/Code/Classes/UAudioPlayback_SoftMixer.pas | 124 | ||||
-rw-r--r-- | Game/Code/Classes/UMusic.pas | 7 |
3 files changed, 100 insertions, 38 deletions
diff --git a/Game/Code/Classes/UAudioInput_Portaudio.pas b/Game/Code/Classes/UAudioInput_Portaudio.pas index 74c8d98f..077598a9 100644 --- a/Game/Code/Classes/UAudioInput_Portaudio.pas +++ b/Game/Code/Classes/UAudioInput_Portaudio.pas @@ -149,6 +149,7 @@ var stream: PPaStream; streamInfo: PPaStreamInfo; sampleRate: integer; + latency: TPaTime; {$IFDEF UsePortmixer} mixer: PPxMixer; {$ENDIF} @@ -210,13 +211,17 @@ begin else sampleRate := 44100; + // on vista and xp the defaultLowInputLatency may be set to 0 but it works. + // TODO: correct too low latencies (what is a too low latency, maybe < 10ms?) + latency := deviceInfo^.defaultLowInputLatency; + // setup desired input parameters with inputParams do begin device := deviceIndex; channelCount := channelCnt; sampleFormat := paInt16; - suggestedLatency := deviceInfo^.defaultLowInputLatency; + suggestedLatency := latency; hostApiSpecificStreamInfo := nil; end; diff --git a/Game/Code/Classes/UAudioPlayback_SoftMixer.pas b/Game/Code/Classes/UAudioPlayback_SoftMixer.pas index e145587c..5beff682 100644 --- a/Game/Code/Classes/UAudioPlayback_SoftMixer.pas +++ b/Game/Code/Classes/UAudioPlayback_SoftMixer.pas @@ -24,6 +24,7 @@ type DecodeStream: TAudioDecodeStream;
SampleBuffer : PChar;
+ SampleBufferCount: integer; // number of available bytes in SampleBuffer
SampleBufferPos : cardinal;
BytesAvail: integer;
cvt: TSDL_AudioCVT;
@@ -65,6 +66,8 @@ type function GetPosition: real;
procedure SetPosition(Time: real);
function ReadData(Buffer: PChar; BufSize: integer): integer;
+
+ procedure GetFFTData(var data: TFFTData);
end;
TAudioMixerStream = class
@@ -507,43 +510,50 @@ begin if (remFrameBytes > 0) then
decodeBufSize := decodeBufSize + (frameSize - remFrameBytes);
- // calc buffer size
- sampleBufSize := decodeBufSize * cvt.len_mult;
-
- // resize buffer if necessary.
- // The required buffer-size will be smaller than the result-buffer
- // in most cases (if the decoded signal is mono or has a lesser bitrate).
- // If the output-rate is 44.1kHz and the decode-rate is 48kHz or 96kHz it
- // will be ~1.09 or ~2.18 times bigger. Those extra memory consumption
- // should be reasonable. If not we should call TDecodeStream.ReadData()
- // multiple times.
- // Note: we do not decrease the buffer by the count of bytes used from
- // the previous call of this function (bytesAvail). Otherwise the
- // buffer will be reallocated each time this function is called just to
- // add or remove a few bytes from the buffer.
- // By not doing this the buffer's size should be rather stable and it
- // will not be reallocated/resized at all if the BufSize params does not
- // change in consecutive calls.
- ReallocMem(SampleBuffer, sampleBufSize);
- if not assigned(SampleBuffer) then
- Exit;
+ Lock();
+ try
+ // calc buffer size
+ sampleBufSize := decodeBufSize * cvt.len_mult;
+
+ // resize buffer if necessary.
+ // The required buffer-size will be smaller than the result-buffer
+ // in most cases (if the decoded signal is mono or has a lesser bitrate).
+ // If the output-rate is 44.1kHz and the decode-rate is 48kHz or 96kHz it
+ // will be ~1.09 or ~2.18 times bigger. Those extra memory consumption
+ // should be reasonable. If not we should call TDecodeStream.ReadData()
+ // multiple times.
+ // Note: we do not decrease the buffer by the count of bytes used from
+ // the previous call of this function (bytesAvail). Otherwise the
+ // buffer will be reallocated each time this function is called just to
+ // add or remove a few bytes from the buffer.
+ // By not doing this the buffer's size should be rather stable and it
+ // will not be reallocated/resized at all if the BufSize params does not
+ // change in consecutive calls.
+ ReallocMem(SampleBuffer, sampleBufSize);
+ if not assigned(SampleBuffer) then
+ Exit;
- // decode data
- nBytesDecoded := DecodeStream.ReadData(SampleBuffer, decodeBufSize);
- if (nBytesDecoded = -1) then
- Exit;
+ // decode data
+ nBytesDecoded := DecodeStream.ReadData(SampleBuffer, decodeBufSize);
+ if (nBytesDecoded = -1) then
+ Exit;
- // end-of-file reached -> stop playback
- if (DecodeStream.EOF) then
- Stop();
+ // end-of-file reached -> stop playback
+ if (DecodeStream.EOF) then
+ Stop();
- // resample decoded data
- cvt.buf := PUint8(SampleBuffer);
- cvt.len := nBytesDecoded;
- if (SDL_ConvertAudio(@cvt) = -1) then
- Exit;
+ // resample decoded data
+ cvt.buf := PUint8(SampleBuffer);
+ cvt.len := nBytesDecoded;
+ if (SDL_ConvertAudio(@cvt) = -1) then
+ Exit;
+
+ SampleBufferCount := cvt.len_cvt;
+ finally
+ Unlock();
+ end;
- BytesAvail := cvt.len_cvt;
+ BytesAvail := SampleBufferCount;
SampleBufferPos := 0;
// copy data to result buffer
@@ -556,6 +566,50 @@ begin Result := BufSize - BytesNeeded;
end;
+procedure TSoftMixerPlaybackStream.GetFFTData(var data: TFFTData);
+var
+ i: integer;
+ Frames: integer;
+ DataIn: PSingleArray;
+ AudioFormat: TAudioFormatInfo;
+begin
+ // only works with SInt16 and Float values at the moment
+ AudioFormat := Engine.GetAudioFormatInfo();
+
+ GetMem(DataIn, FFTSize * SizeOf(Single));
+
+ Lock();
+ // FIXME: We just use the first Frames frames, the others are ignored.
+ // This is OK for the equalizer display but not if we want to use
+ // this function for voice-analysis.
+ Frames := Min(FFTSize, SampleBufferCount div AudioFormat.FrameSize);
+ // use only first channel and convert data to float-values
+ case AudioFormat.Format of
+ asfS16:
+ begin
+ for i := 0 to Frames-1 do
+ DataIn[i] := PSmallInt(@SampleBuffer[i*AudioFormat.FrameSize])^ / -Low(SmallInt);
+ end;
+ asfFloat:
+ begin
+ for i := 0 to Frames-1 do
+ DataIn[i] := PSingle(@SampleBuffer[i*AudioFormat.FrameSize])^;
+ end;
+ end;
+ Unlock();
+
+ WindowFunc(FFTSize, DataIn);
+ PowerSpectrum(FFTSize, DataIn, @data);
+ FreeMem(DataIn);
+
+ // resize data to a 0..1 range
+ for i := 0 to High(TFFTData) do
+ begin
+ // TODO: this might need some work
+ data[i] := Sqrt(data[i]) / 100;
+ end;
+end;
+
(* TODO: libsamplerate support
function TSoftMixerPlaybackStream.ReadData(Buffer: PChar; BufSize: integer): integer;
var
@@ -778,8 +832,8 @@ end; //Equalizer
procedure TAudioPlayback_SoftMixer.GetFFTData(var data: TFFTData);
begin
- //Get Channel Data Mono and 256 Values
-// BASS_ChannelGetData(Bass, @Result, BASS_DATA_FFT512);
+ if assigned(MusicStream) then
+ MusicStream.GetFFTData(data);
end;
// Interface for Visualizer
diff --git a/Game/Code/Classes/UMusic.pas b/Game/Code/Classes/UMusic.pas index 44ae2a61..f0bf3a9c 100644 --- a/Game/Code/Classes/UMusic.pas +++ b/Game/Code/Classes/UMusic.pas @@ -85,10 +85,13 @@ type Razem: real; // (TODO: Razem = total time)
end;
-
+
+const
+ FFTSize = 512; // size of FFT data (output: FFTSize/2 values)
type
- TFFTData = array[0..255] of Single;
+ TFFTData = array[0..(FFTSize div 2-1)] of Single;
+type
TPCMStereoSample = array[0..1] of Smallint;
TPCMData = array[0..511] of TPCMStereoSample;
|