aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code
diff options
context:
space:
mode:
authortobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-03-05 11:16:44 +0000
committertobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-03-05 11:16:44 +0000
commitf786f747554c79de3a7aa1fd63cd1e7c2e0763c3 (patch)
tree55d68e30dd0117689fb36a2ebe3c2849f1c74652 /Game/Code
parent80c3bf3f4b428d03c0f237ebb001e002abe5f9e4 (diff)
downloadusdx-f786f747554c79de3a7aa1fd63cd1e7c2e0763c3.tar.gz
usdx-f786f747554c79de3a7aa1fd63cd1e7c2e0763c3.tar.xz
usdx-f786f747554c79de3a7aa1fd63cd1e7c2e0763c3.zip
FFT support for SDL/Portaudio playback
git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@909 b956fd51-792f-4845-bead-9b4dfca2ff2c
Diffstat (limited to 'Game/Code')
-rw-r--r--Game/Code/Classes/UAudioInput_Portaudio.pas7
-rw-r--r--Game/Code/Classes/UAudioPlayback_SoftMixer.pas124
-rw-r--r--Game/Code/Classes/UMusic.pas7
-rw-r--r--Game/Code/Screens/UScreenOptionsRecord.pas12
4 files changed, 106 insertions, 44 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;
diff --git a/Game/Code/Screens/UScreenOptionsRecord.pas b/Game/Code/Screens/UScreenOptionsRecord.pas
index 3da29f43..53d0b2ad 100644
--- a/Game/Code/Screens/UScreenOptionsRecord.pas
+++ b/Game/Code/Screens/UScreenOptionsRecord.pas
@@ -384,6 +384,7 @@ var
DeviceCfg: PInputDeviceConfig;
SelectSlide: TSelectSlide;
ToneBoxWidth: real;
+ Volume: single;
begin
DrawBG;
DrawFG;
@@ -434,22 +435,21 @@ begin
glVertex2f(x2, y2);
glVertex2f(x1, y2);
+ Volume := PreviewChannel[ChannelIndex].MaxSampleVolume();
+
// coordinates for volume bar
x1 := x1 + 1;
- x2 := x1 + Trunc((SelectSlide.TextureSBG.W-4) *
- PreviewChannel[ChannelIndex].MaxSampleVolume()) + 1;
+ x2 := x1 + Trunc((SelectSlide.TextureSBG.W-4) * Volume) + 1;
y1 := y1 + 1;
y2 := y2 - 1;
// draw volume bar
glColor3f(RD, GD, BD);
glVertex2f(x1, y1);
- glColor3f(R, G, B);
- glVertex2f(x2, y1);
+ glVertex2f(x1, y2);
glColor3f(R, G, B);
glVertex2f(x2, y2);
- glColor3f(RD, GD, BD);
- glVertex2f(x1, y2);
+ glVertex2f(x2, y1);
//////////
// draw Pitch