aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code/Classes
diff options
context:
space:
mode:
Diffstat (limited to 'Game/Code/Classes')
-rw-r--r--Game/Code/Classes/UAudioDecoder_FFMpeg.pas197
-rw-r--r--Game/Code/Classes/UAudioInput_Bass.pas490
-rw-r--r--Game/Code/Classes/UAudioInput_Portaudio.pas331
-rw-r--r--Game/Code/Classes/UAudioPlayback_Bass.pas450
-rw-r--r--Game/Code/Classes/UAudioPlayback_Portaudio.pas608
-rw-r--r--Game/Code/Classes/UDraw.pas20
-rw-r--r--Game/Code/Classes/UIni.pas1599
-rw-r--r--Game/Code/Classes/UMain.pas35
-rw-r--r--Game/Code/Classes/UMedia_dummy.pas474
-rw-r--r--Game/Code/Classes/UMusic.pas915
-rw-r--r--Game/Code/Classes/URecord.pas850
11 files changed, 3003 insertions, 2966 deletions
diff --git a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas
index c705d603..646e9eef 100644
--- a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas
+++ b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas
@@ -17,36 +17,33 @@ interface
{$MODE Delphi}
{$ENDIF}
-{$I switches.inc}
-
-
-uses Classes,
- {$IFDEF win32}
- windows,
- {$ENDIF}
- SysUtils,
- SDL,
- avcodec, // FFMpeg Audio file decoding
- avformat,
- avutil,
- avio, // used for url_ferror
- mathematics, // used for av_rescale_q
- ULog,
- UMusic;
+{$I ../switches.inc}
+
+
+uses
+ Classes,
+ {$IFDEF win32}
+ windows,
+ {$ENDIF}
+ SysUtils,
+ UMusic;
implementation
uses
- {$IFDEF LAZARUS}
- lclintf,
- {$ifndef win32}
- libc,
- {$endif}
- {$ENDIF}
- UIni,
- UMain,
- UThemes,
- UConfig;
+ {$ifndef win32}
+ libc,
+ {$endif}
+ UIni,
+ UMain,
+ avcodec, // FFMpeg Audio file decoding
+ avformat,
+ avutil,
+ avio, // used for url_ferror
+ mathematics, // used for av_rescale_q
+ SDL,
+ ULog,
+ UConfig;
type
@@ -106,12 +103,12 @@ type
ffmpegStreamIndex : Integer;
ffmpegStream : PAVStream;
- // "static" vars for DecodeFrame
+ // state-vars for DecodeFrame
pkt : TAVPacket;
audio_pkt_data : PChar;
audio_pkt_size : integer;
- // "static" vars for AudioCallback
+ // state-vars for AudioCallback
audio_buf_index : cardinal;
audio_buf_size : cardinal;
audio_buf : TAudioBuffer;
@@ -127,8 +124,7 @@ type
procedure Close(); override;
function GetLength(): real; override;
- function GetChannelCount(): cardinal; override;
- function GetSampleRate(): cardinal; override;
+ function GetAudioFormatInfo(): TAudioFormatInfo; override;
function GetPosition: real; override;
procedure SetPosition(Time: real); override;
function IsEOF(): boolean; override;
@@ -169,7 +165,7 @@ begin
audio_buf_index := 0;
audio_buf_size := 0;
- FillChar(pkt, sizeof(TAVPacket), #0);
+ FillChar(pkt, sizeof(TAVPacket), 0);
Self.pFormatCtx := pFormatCtx;
Self.pCodecCtx := pCodecCtx;
@@ -221,14 +217,12 @@ begin
result := pFormatCtx^.duration / AV_TIME_BASE;
end;
-function TFFMpegDecodeStream.GetChannelCount(): cardinal;
-begin
- result := pCodecCtx^.channels;
-end;
-
-function TFFMpegDecodeStream.GetSampleRate(): cardinal;
+function TFFMpegDecodeStream.GetAudioFormatInfo(): TAudioFormatInfo;
begin
- result := pCodecCtx^.sample_rate;
+ result.Channels := pCodecCtx^.channels;
+ result.SampleRate := pCodecCtx^.sample_rate;
+ //result.Format := pCodecCtx^.sample_fmt; // sample_fmt not yet used by FFMpeg
+ result.Format := asfS16; // use FFMpeg's standard format
end;
function TFFMpegDecodeStream.IsEOF(): boolean;
@@ -260,7 +254,7 @@ begin
SDL_mutexP(lock);
seekPos := Trunc(Time * AV_TIME_BASE);
// FIXME: seek_flags = rel < 0 ? AVSEEK_FLAG_BACKWARD : 0
- seekFlags := 0;
+ seekFlags := 0;//AVSEEK_FLAG_BACKWARD;
seekRequest := true;
SDL_CondSignal(resumeCond);
SDL_mutexV(lock);
@@ -279,9 +273,9 @@ begin
while (true) do
begin
+ //SafeWriteLn('Hallo');
SDL_mutexP(stream.lock);
-
// wait if end-of-file reached
if (eofState) then
begin
@@ -309,18 +303,19 @@ begin
// TODO: Do we need this?
// The position is converted to AV_TIME_BASE and then to the stream-specific base.
// Why not convert to the stream-specific one from the beginning.
- seekTarget := av_rescale_q(stream.seekPos, AV_TIME_BASE_Q, stream.ffmpegStream^.time_base);
- if(av_seek_frame(stream.pFormatCtx, stream.ffmpegStreamIndex,
- seekTarget, stream.seekFlags) = 0) then
- begin
- Log.LogStatus(stream.pFormatCtx^.filename + ': error while seeking', 'UAudioDecoder_FFMpeg');
- end
- else
- begin
- stream.packetQueue.Flush();
- stream.packetQueue.Put(@FlushPacket);
- end;
- stream.seekRequest := false;
+ seekTarget := av_rescale_q(stream.seekPos, AV_TIME_BASE_Q, stream.ffmpegStream^.time_base);
+ if(av_seek_frame(stream.pFormatCtx, stream.ffmpegStreamIndex,
+ seekTarget, stream.seekFlags) < 0) then
+ begin
+ // this will crash in FPC due to a bug
+ //Log.LogStatus({stream.pFormatCtx^.filename +} ': error while seeking', 'UAudioDecoder_FFMpeg');
+ end
+ else
+ begin
+ stream.packetQueue.Flush();
+ stream.packetQueue.Put(@FlushPacket);
+ end;
+ stream.seekRequest := false;
end;
SDL_mutexV(stream.lock);
@@ -340,8 +335,10 @@ begin
{$ELSE}
pbIOCtx := @stream.pFormatCtx^.pb;
{$IFEND}
+
if(url_feof(pbIOCtx) <> 0) then
begin
+ SafeWriteLn('feof');
eofState := true;
continue;
end;
@@ -349,24 +346,25 @@ begin
// check for errors
if(url_ferror(pbIOCtx) = 0) then
begin
+ SafeWriteLn('Errorf');
// no error -> wait for user input
- SDL_Delay(100);
- continue;
- end
- else
- begin
- // an error occured -> abort
- // TODO: eof or quit?
- eofState := true;
- continue;
- end;
- end;
-
- //writeln( 'ffmpeg - av_read_frame' );
+ SDL_Delay(100);
+ continue;
+ end
+ else
+ begin
+ // an error occured -> abort
+ // TODO: eof or quit?
+ eofState := true;
+ continue;
+ end;
+ end;
+
+ //SafeWriteLn( 'ffmpeg - av_read_frame' );
if(packet.stream_index = stream.ffmpegStreamIndex) then
begin
- //writeln( 'packet_queue_put' );
+ //SafeWriteLn( 'packet_queue_put' );
stream.packetQueue.put(@packet);
end
else
@@ -375,7 +373,7 @@ begin
end;
end;
- //Writeln('Done: ' + inttostr(stream.packetQueue.nbPackets));
+ SafeWriteLn('Done: ' + inttostr(stream.packetQueue.nbPackets));
result := 0;
end;
@@ -394,21 +392,25 @@ begin
begin
while (audio_pkt_size > 0) do
begin
- //writeln( 'got audio packet' );
+ //SafeWriteLn( 'got audio packet' );
data_size := bufSize;
- // TODO: should be avcodec_decode_audio2 but this wont link on my ubuntu box.
+ {$IF LIBAVCODEC_VERSION >= 51030000} // 51.30.0
+ len1 := avcodec_decode_audio2(pCodecCtx, @buffer,
+ data_size, audio_pkt_data, audio_pkt_size);
+ {$ELSE}
// FIXME: with avcodec_decode_audio a package could contain several frames
// this is not handled yet
len1 := avcodec_decode_audio(pCodecCtx, @buffer,
data_size, audio_pkt_data, audio_pkt_size);
+ {$IFEND}
- //writeln('avcodec_decode_audio : ' + inttostr( len1 ));
+ //SafeWriteLn('avcodec_decode_audio : ' + inttostr( len1 ));
if(len1 < 0) then
begin
- // if error, skip frame
- //writeln( 'Skip audio frame' );
+ // if error, skip frame
+ SafeWriteLn( 'Skip audio frame' );
audio_pkt_size := 0;
break;
end;
@@ -444,6 +446,7 @@ begin
if (audio_pkt_data = PChar(FlushPacket.data)) then
begin
avcodec_flush_buffers(pCodecCtx);
+ SafeWriteLn('Flush');
continue;
end;
@@ -452,11 +455,12 @@ begin
begin
// end-of-file reached -> set EOF-flag
SetEOF(true);
+ SafeWriteLn('EOF');
// note: buffer is not (even partially) filled -> no data to return
exit;
end;
- //writeln( 'Audio Packet Size - ' + inttostr(audio_pkt_size) );
+ //SafeWriteLn( 'Audio Packet Size - ' + inttostr(audio_pkt_size) );
end;
end;
@@ -480,14 +484,14 @@ begin
begin
// We have already sent all our data; get more
audio_size := DecodeFrame(audio_buf, sizeof(TAudioBuffer));
- //writeln('audio_decode_frame : '+ inttostr(audio_size));
+ //SafeWriteLn('audio_decode_frame : '+ inttostr(audio_size));
if(audio_size < 0) then
begin
// If error, output silence
audio_buf_size := 1024;
FillChar(audio_buf, audio_buf_size, #0);
- //writeln( 'Silence' );
+ //SafeWriteLn( 'Silence' );
end
else
begin
@@ -531,11 +535,11 @@ begin
// init end-of-file package
av_init_packet(EOFPacket);
- EOFPacket.data := Pointer(PChar('EOF'));
+ EOFPacket.data := Pointer(PChar('EOF'));
// init flush package
av_init_packet(FlushPacket);
- FlushPacket.data := Pointer(PChar('FLUSH'));
+ FlushPacket.data := Pointer(PChar('FLUSH'));
result := true;
end;
@@ -611,7 +615,7 @@ begin
end;
avcodec_open(pCodecCtx, pCodec);
- //writeln( 'Opened the codec' );
+ //WriteLn( 'Opened the codec' );
stream := TFFMpegDecodeStream.Create(pFormatCtx, pCodecCtx, pCodec,
ffmpegStreamID, ffmpegStream);
@@ -671,7 +675,7 @@ begin
Self.lastPkt := pkt1;
inc(Self.nbPackets);
- //Writeln('Put: ' + inttostr(nbPackets));
+ //SafeWriteLn('Put: ' + inttostr(nbPackets));
Self.size := Self.size + pkt1^.pkt.size;
SDL_CondSignal(Self.cond);
@@ -705,7 +709,7 @@ begin
Self.lastPkt := nil;
dec(Self.nbPackets);
- //Writeln('Get: ' + inttostr(nbPackets));
+ //SafeWriteLn('Get: ' + inttostr(nbPackets));
Self.size := Self.size - pkt1^.pkt.size;
pkt := pkt1^.pkt;
@@ -734,22 +738,23 @@ procedure TPacketQueue.Flush();
var
pkt, pkt1: PAVPacketList;
begin
- SDL_LockMutex(Self.mutex);
-
- pkt := Self.firstPkt;
- while(pkt <> nil) do
- begin
- pkt1 := pkt^.next;
- av_free_packet(@pkt^.pkt);
- av_freep(pkt);
- pkt := pkt1;
- end;
- Self.lastPkt := nil;
- Self.firstPkt := nil;
- Self.nbPackets := 0;
- Self.size := 0;
-
- SDL_UnlockMutex(Self.mutex);
+ SDL_LockMutex(Self.mutex);
+
+ pkt := Self.firstPkt;
+ while(pkt <> nil) do
+ begin
+ pkt1 := pkt^.next;
+ av_free_packet(@pkt^.pkt);
+ // Note: param must be a pointer to a pointer!
+ av_freep(@pkt);
+ pkt := pkt1;
+ end;
+ Self.lastPkt := nil;
+ Self.firstPkt := nil;
+ Self.nbPackets := 0;
+ Self.size := 0;
+
+ SDL_UnlockMutex(Self.mutex);
end;
diff --git a/Game/Code/Classes/UAudioInput_Bass.pas b/Game/Code/Classes/UAudioInput_Bass.pas
index 75a5aee5..6d661258 100644
--- a/Game/Code/Classes/UAudioInput_Bass.pas
+++ b/Game/Code/Classes/UAudioInput_Bass.pas
@@ -1,287 +1,203 @@
-unit UAudioInput_Bass;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-
-uses Classes,
- {$IFDEF win32}
- windows,
- {$ENDIF}
- SysUtils,
- bass,
- ULog,
- UMusic;
-
-implementation
-
-uses
- {$IFDEF LAZARUS}
- lclintf,
- {$ENDIF}
- URecord,
- UIni,
- UMain,
- UCommon,
- UThemes;
-
-type
- TAudioInput_Bass = class( TInterfacedObject, IAudioInput)
- private
- public
- function GetName: String;
-
- {IAudioInput interface}
- procedure InitializeRecord;
-
- procedure CaptureStart;
- procedure CaptureStop;
- procedure CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound);
- procedure StopCard(Card: byte);
- end;
-
- TBassSoundCard = class(TGenericSoundCard)
- RecordStream: HSTREAM;
- end;
-
-var
- singleton_AudioInputBass : IAudioInput;
-
-
-function TAudioInput_Bass.GetName: String;
-begin
- result := 'BASS_Input';
-end;
-
-procedure TAudioInput_Bass.InitializeRecord;
-var
- device: integer;
- Descr: string;
- input: integer;
- input2: integer;
- InputName: PChar;
- Flags: integer;
- mic: array[0..15] of integer;
- SC: integer; // soundcard
- SCI: integer; // soundcard input
- No: integer;
-
-function isDuplicate(Desc: String): Boolean;
-var
- I: Integer;
-begin
- Result := False;
- //Check for Soundcard with same Description
- For I := 0 to SC-1 do
- begin
- if (AudioInputProcessor.SoundCard[I].Description = Desc) then
- begin
- Result := True;
- Break;
- end;
- end;
-end;
-
-begin
- with AudioInputProcessor do
- begin
- // checks for recording devices and puts them into an array
- SetLength(SoundCard, 0);
-
- SC := 0;
- Descr := BASS_RecordGetDeviceDescription(SC);
-
- while (Descr <> '') do
- begin
- //If there is another SoundCard with the Same ID, Search an available Name
- if (IsDuplicate(Descr)) then
- begin
- No:= 1; //Count of SoundCards with same Name
- Repeat
- Inc(No)
- Until not IsDuplicate(Descr + ' (' + InttoStr(No) + ')');
-
- //Set Description
- Descr := Descr + ' (' + InttoStr(No) + ')';
- end;
-
- SetLength(SoundCard, SC+1);
-
- // TODO: free object on termination
- SoundCard[SC] := TBassSoundCard.Create();
- SoundCard[SC].Description := Descr;
-
- //Get Recording Inputs
- SCI := 0;
- BASS_RecordInit(SC);
-
- InputName := BASS_RecordGetInputName(SCI);
-
- {$IFDEF DARWIN}
- // Under MacOSX the SingStar Mics have an empty
- // InputName. So, we have to add a hard coded
- // Workaround for this problem
- if (InputName = nil) and (Pos( 'USBMIC Serial#', Descr) > 0) then
- begin
- InputName := 'Microphone';
- end;
- {$ENDIF}
-
- SetLength(SoundCard[SC].Input, 1);
- SoundCard[SC].Input[SCI].Name := InputName;
-
- // process each input
- while (InputName <> nil) do
- begin
- Flags := BASS_RecordGetInput(SCI);
- if (SCI >= 1) {AND (Flags AND BASS_INPUT_OFF = 0)} then
- begin
- SetLength(SoundCard[SC].Input, SCI+1);
- SoundCard[SC].Input[SCI].Name := InputName;
- end;
-
- //Set Mic Index
- if ((Flags and BASS_INPUT_TYPE_MIC) = 1) then
- SoundCard[SC].MicInput := SCI;
-
- Inc(SCI);
- InputName := BASS_RecordGetInputName(SCI);
- end;
-
- BASS_RecordFree;
-
- Inc(SC);
- Descr := BASS_RecordGetDeviceDescription(SC);
- end; // while
- end; // with Recording
-end;
-
-// TODO: code is used by all IAudioInput implementors
-// -> move to a common superclass (TAudioInput_Generic?)
-procedure TAudioInput_Bass.CaptureStart;
-var
- S: integer;
- SC: integer;
- PlayerLeft, PlayerRight: integer;
- CaptureSoundLeft, CaptureSoundRight: TSound;
-begin
- for S := 0 to High(AudioInputProcessor.Sound) do
- AudioInputProcessor.Sound[S].BufferLong[0].Clear;
-
- for SC := 0 to High(Ini.CardList) do
- begin
- PlayerLeft := Ini.CardList[SC].ChannelL-1;
- PlayerRight := Ini.CardList[SC].ChannelR-1;
- if PlayerLeft >= PlayersPlay then PlayerLeft := -1;
- if PlayerRight >= PlayersPlay then PlayerRight := -1;
- if (PlayerLeft > -1) or (PlayerRight > -1) then begin
- if (PlayerLeft > -1) then
- CaptureSoundLeft := AudioInputProcessor.Sound[PlayerLeft]
- else
- CaptureSoundLeft := nil;
- if (PlayerRight > -1) then
- CaptureSoundRight := AudioInputProcessor.Sound[PlayerRight]
- else
- CaptureSoundRight := nil;
-
- CaptureCard(SC, CaptureSoundLeft, CaptureSoundRight);
- end;
- end;
-end;
-
-// TODO: code is used by all IAudioInput implementors
-// -> move to a common superclass (TAudioInput_Generic?)
-procedure TAudioInput_Bass.CaptureStop;
-var
- SC: integer;
- PlayerLeft: integer;
- PlayerRight: integer;
-begin
-
- for SC := 0 to High(Ini.CardList) do begin
- PlayerLeft := Ini.CardList[SC].ChannelL-1;
- PlayerRight := Ini.CardList[SC].ChannelR-1;
- if PlayerLeft >= PlayersPlay then PlayerLeft := -1;
- if PlayerRight >= PlayersPlay then PlayerRight := -1;
- if (PlayerLeft > -1) or (PlayerRight > -1) then
- StopCard(SC);
- end;
-
-end;
-
-{*
- * Bass input capture callback.
- * Params:
- * stream - BASS input stream
- * buffer - buffer of captured samples
- * len - size of buffer in bytes
- * user - players associated with left/right channels
- *}
-function MicrophoneCallback(stream: HSTREAM; buffer: Pointer;
- len: Cardinal; Card: Cardinal): boolean; stdcall;
-begin
- AudioInputProcessor.HandleMicrophoneData(buffer, len, AudioInputProcessor.SoundCard[Card]);
- Result := true;
-end;
-
-{*
- * Start input-capturing on Soundcard specified by Card.
- * Params:
- * Card - soundcard index in AudioInputProcessor.SoundCard array
- * CaptureSoundLeft - sound(-buffer) used for left channel capture data
- * CaptureSoundRight - sound(-buffer) used for right channel capture data
- *}
-procedure TAudioInput_Bass.CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound);
-var
- Error: integer;
- ErrorMsg: string;
- bassSoundCard: TBassSoundCard;
-begin
- if not BASS_RecordInit(Card) then
- begin
- Error := BASS_ErrorGetCode;
- ErrorMsg := IntToStr(Error);
- if Error = BASS_ERROR_DX then ErrorMsg := 'No DX5';
- if Error = BASS_ERROR_ALREADY then ErrorMsg := 'The device has already been initialized';
- if Error = BASS_ERROR_DEVICE then ErrorMsg := 'The device number specified is invalid';
- if Error = BASS_ERROR_DRIVER then ErrorMsg := 'There is no available device driver';
- Log.LogError('Error initializing record [' + IntToStr(Card) + ']');
- Log.LogError('TAudio_bass.CaptureCard: Error initializing record: ' + ErrorMsg);
- end
- else
- begin
- bassSoundCard := TBassSoundCard(AudioInputProcessor.SoundCard[Card]);
- bassSoundCard.CaptureSoundLeft := CaptureSoundLeft;
- bassSoundCard.CaptureSoundRight := CaptureSoundRight;
-
- // capture in 44.1kHz/stereo/16bit and a 20ms callback period
- bassSoundCard.RecordStream :=
- BASS_RecordStart(44100, 2, MakeLong(0, 20) , @MicrophoneCallback, Card);
- end;
-end;
-
-{*
- * Stop input-capturing on Soundcard specified by Card.
- * Params:
- * Card - soundcard index in AudioInputProcessor.SoundCard array
- *}
-procedure TAudioInput_Bass.StopCard(Card: byte);
-begin
- BASS_RecordSetDevice(Card);
- BASS_RecordFree;
-end;
-
-
-initialization
- singleton_AudioInputBass := TAudioInput_Bass.create();
- AudioManager.add( singleton_AudioInputBass );
-
-finalization
- AudioManager.Remove( singleton_AudioInputBass );
-
-end.
+unit UAudioInput_Bass;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+
+uses
+ Classes,
+ SysUtils,
+ URecord,
+ UMusic;
+
+implementation
+
+uses
+ UMain,
+ UIni,
+ ULog,
+ UAudioCore_Bass,
+ Windows,
+ bass;
+
+type
+ TAudioInput_Bass = class(TAudioInputBase)
+ public
+ function GetName: String; override;
+ function InitializeRecord: boolean; override;
+ destructor Destroy; override;
+ end;
+
+ TBassInputDevice = class(TAudioInputDevice)
+ public
+ DeviceIndex: integer; // index in TAudioInputProcessor.Device[]
+ BassDeviceID: integer; // DeviceID used by BASS
+ RecordStream: HSTREAM;
+
+ procedure Start(); override;
+ procedure Stop(); override;
+ end;
+
+var
+ singleton_AudioInputBass : IAudioInput;
+
+
+{ Global }
+
+{*
+ * Bass input capture callback.
+ * Params:
+ * stream - BASS input stream
+ * buffer - buffer of captured samples
+ * len - size of buffer in bytes
+ * user - players associated with left/right channels
+ *}
+function MicrophoneCallback(stream: HSTREAM; buffer: Pointer;
+ len: Cardinal; Card: Cardinal): boolean; stdcall;
+begin
+ AudioInputProcessor.HandleMicrophoneData(buffer, len,
+ AudioInputProcessor.Device[Card]);
+ Result := true;
+end;
+
+
+{ TBassInputDevice }
+
+{*
+ * Start input-capturing on this device.
+ * TODO: call BASS_RecordInit only once
+ *}
+procedure TBassInputDevice.Start();
+const
+ captureFreq = 44100;
+begin
+ // recording already started -> stop first
+ if (RecordStream <> 0) then
+ Stop();
+
+ // TODO: Call once. Otherwise it's to slow
+ if not BASS_RecordInit(BassDeviceID) then
+ begin
+ Log.LogError('TBassInputDevice.Start: Error initializing device['+IntToStr(DeviceIndex)+']: ' +
+ TAudioCore_Bass.ErrorGetString());
+ Exit;
+ end;
+
+ SampleRate := captureFreq;
+
+ // capture in 44.1kHz/stereo/16bit and a 20ms callback period
+ RecordStream := BASS_RecordStart(captureFreq, 2, MakeLong(0, 20),
+ @MicrophoneCallback, DeviceIndex);
+ if (RecordStream = 0) then
+ begin
+ BASS_RecordFree;
+ Exit;
+ end;
+end;
+
+{*
+ * Stop input-capturing on this device.
+ *}
+procedure TBassInputDevice.Stop();
+begin
+ if (RecordStream = 0) then
+ Exit;
+ // TODO: Don't free the device. Do this on close
+ if (BASS_RecordSetDevice(BassDeviceID)) then
+ BASS_RecordFree;
+ RecordStream := 0;
+end;
+
+
+{ TAudioInput_Bass }
+
+function TAudioInput_Bass.GetName: String;
+begin
+ result := 'BASS_Input';
+end;
+
+function TAudioInput_Bass.InitializeRecord(): boolean;
+var
+ Descr: PChar;
+ SourceName: PChar;
+ Flags: integer;
+ BassDeviceID: integer;
+ BassDevice: TBassInputDevice;
+ DeviceIndex: integer;
+ SourceIndex: integer;
+begin
+ result := false;
+
+ DeviceIndex := 0;
+ BassDeviceID := 0;
+ SetLength(AudioInputProcessor.Device, 0);
+
+ // checks for recording devices and puts them into an array
+ while true do
+ begin
+ Descr := BASS_RecordGetDeviceDescription(BassDeviceID);
+ if (Descr = nil) then
+ break;
+
+ SetLength(AudioInputProcessor.Device, DeviceIndex+1);
+
+ // TODO: free object on termination
+ BassDevice := TBassInputDevice.Create();
+ AudioInputProcessor.Device[DeviceIndex] := BassDevice;
+
+ BassDevice.DeviceIndex := DeviceIndex;
+ BassDevice.BassDeviceID := BassDeviceID;
+ BassDevice.Description := UnifyDeviceName(Descr, DeviceIndex);
+
+ // get input sources
+ SourceIndex := 0;
+ BASS_RecordInit(BassDeviceID);
+ BassDevice.MicInput := 0;
+
+ // process each input
+ while true do
+ begin
+ SourceName := BASS_RecordGetInputName(SourceIndex);
+ if (SourceName = nil) then
+ break;
+
+ SetLength(BassDevice.Source, SourceIndex+1);
+ BassDevice.Source[SourceIndex].Name :=
+ UnifyDeviceSourceName(SourceName, BassDevice.Description);
+
+ // set mic index
+ Flags := BASS_RecordGetInput(SourceIndex);
+ if ((Flags and BASS_INPUT_TYPE_MIC) <> 0) then
+ BassDevice.MicInput := SourceIndex;
+
+ Inc(SourceIndex);
+ end;
+
+ BASS_RecordFree;
+
+ Inc(DeviceIndex);
+ Inc(BassDeviceID);
+ end;
+
+ result := true;
+end;
+
+destructor TAudioInput_Bass.Destroy;
+begin
+ inherited;
+end;
+
+
+initialization
+ singleton_AudioInputBass := TAudioInput_Bass.create();
+ AudioManager.add( singleton_AudioInputBass );
+
+finalization
+ AudioManager.Remove( singleton_AudioInputBass );
+
+end.
diff --git a/Game/Code/Classes/UAudioInput_Portaudio.pas b/Game/Code/Classes/UAudioInput_Portaudio.pas
index 8073a7f3..c969a1b3 100644
--- a/Game/Code/Classes/UAudioInput_Portaudio.pas
+++ b/Game/Code/Classes/UAudioInput_Portaudio.pas
@@ -6,60 +6,43 @@ interface
{$MODE Delphi}
{$ENDIF}
-{$I switches.inc}
+{$I ../switches.inc}
-uses Classes,
- SysUtils,
- portaudio,
- {$IFDEF UsePortmixer}
- portmixer,
- {$ENDIF}
- ULog,
- UMusic;
+uses
+ Classes,
+ SysUtils,
+ UMusic;
implementation
uses
- {$IFDEF LAZARUS}
- lclintf,
- {$ENDIF}
- URecord,
- UIni,
- UMain,
- UCommon,
- UThemes;
-{
-type
- TPaHostApiIndex = PaHostApiIndex;
- TPaDeviceIndex = PaDeviceIndex;
- PPaStream = ^PaStreamPtr;
- PPaStreamCallbackTimeInfo = ^PaStreamCallbackTimeInfo;
- TPaStreamCallbackFlags = PaStreamCallbackFlags;
- TPaHostApiTypeId = PaHostApiTypeId;
- PPaHostApiInfo = ^PaHostApiInfo;
- PPaDeviceInfo = ^PaDeviceInfo;
- TPaError = PaError;
- TPaStreamParameters = PaStreamParameters;
-}
+ URecord,
+ UIni,
+ ULog,
+ UMain,
+ {$IFDEF UsePortmixer}
+ portmixer,
+ {$ENDIF}
+ portaudio;
+
type
- TAudioInput_Portaudio = class( TInterfacedObject, IAudioInput )
+ TAudioInput_Portaudio = class(TAudioInputBase)
private
function GetPreferredApiIndex(): TPaHostApiIndex;
public
- function GetName: String;
- procedure InitializeRecord;
-
- procedure CaptureStart;
- procedure CaptureStop;
-
- procedure CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound);
- procedure StopCard(Card: byte);
+ function GetName: String; override;
+ function InitializeRecord: boolean; override;
+ destructor Destroy; override;
end;
- TPortaudioSoundCard = class(TGenericSoundCard)
- RecordStream: PPaStream;
- DeviceIndex: TPaDeviceIndex;
+ TPortaudioInputDevice = class(TAudioInputDevice)
+ public
+ RecordStream: PPaStream;
+ PaDeviceIndex: TPaDeviceIndex;
+
+ procedure Start(); override;
+ procedure Stop(); override;
end;
function MicrophoneCallback(input: Pointer; output: Pointer; frameCount: Longword;
@@ -69,9 +52,6 @@ function MicrophoneCallback(input: Pointer; output: Pointer; frameCount: Longwor
var
singleton_AudioInputPortaudio : IAudioInput;
-const
- sampleRate: Double = 44100.;
-
{* the default API used by Portaudio is the least common denominator
* and might lack efficiency. ApiPreferenceOrder defines the order of
* preferred APIs to use. The first API-type in the list is tried first. If it's
@@ -102,6 +82,62 @@ var
array[0..0] of TPaHostApiTypeId = ( paDefaultApi );
{$IFEND}
+
+{ TPortaudioInputDevice }
+
+procedure TPortaudioInputDevice.Start();
+var
+ Error: TPaError;
+ ErrorMsg: string;
+ inputParams: TPaStreamParameters;
+ deviceInfo: PPaDeviceInfo;
+begin
+ // get input latency info
+ deviceInfo := Pa_GetDeviceInfo(PaDeviceIndex);
+
+ // set input stream parameters
+ with inputParams do begin
+ device := PaDeviceIndex;
+ channelCount := 2;
+ sampleFormat := paInt16;
+ suggestedLatency := deviceInfo^.defaultLowInputLatency;
+ hostApiSpecificStreamInfo := nil;
+ end;
+
+ Log.LogStatus(inttostr(PaDeviceIndex), 'Portaudio');
+ Log.LogStatus(floattostr(deviceInfo^.defaultLowInputLatency), 'Portaudio');
+
+ // open input stream
+ Error := Pa_OpenStream(RecordStream, @inputParams, nil, SampleRate,
+ paFramesPerBufferUnspecified, paNoFlag,
+ @MicrophoneCallback, Pointer(Self));
+ if(Error <> paNoError) then begin
+ ErrorMsg := Pa_GetErrorText(Error);
+ Log.CriticalError('TPortaudioInputDevice.Start(): Error opening stream: ' + ErrorMsg);
+ //Halt;
+ end;
+
+ // start capture
+ Error := Pa_StartStream(RecordStream);
+ if(Error <> paNoError) then begin
+ Pa_CloseStream(RecordStream);
+ ErrorMsg := Pa_GetErrorText(Error);
+ Log.CriticalError('TPortaudioInputDevice.Start(): Error starting stream: ' + ErrorMsg);
+ //Halt;
+ end;
+end;
+
+procedure TPortaudioInputDevice.Stop();
+begin
+ if assigned(RecordStream) then begin
+ Pa_StopStream(RecordStream);
+ Pa_CloseStream(RecordStream);
+ end;
+end;
+
+
+{ TAudioInput_Portaudio }
+
function TAudioInput_Portaudio.GetName: String;
begin
result := 'Portaudio';
@@ -130,8 +166,7 @@ begin
end;
end;
-// TODO: should be a function with boolean return type
-procedure TAudioInput_Portaudio.InitializeRecord;
+function TAudioInput_Portaudio.InitializeRecord(): boolean;
var
i: integer;
apiIndex: TPaHostApiIndex;
@@ -139,37 +174,42 @@ var
deviceName: string;
deviceIndex: TPaDeviceIndex;
deviceInfo: PPaDeviceInfo;
- inputCnt: integer;
- inputName: string;
+ sourceCnt: integer;
+ sourceName: string;
SC: integer; // soundcard
- SCI: integer; // soundcard input
+ SCI: integer; // soundcard source
err: TPaError;
errMsg: string;
- paSoundCard: TPortaudioSoundCard;
+ paDevice: TPortaudioInputDevice;
inputParams: TPaStreamParameters;
stream: PPaStream;
{$IFDEF UsePortmixer}
mixer: PPxMixer;
{$ENDIF}
+const
+ captureFreq = 44100;
begin
- // TODO: call Pa_Terminate() on termination
+ result := false;
+
+ writeln('0');
+
err := Pa_Initialize();
if(err <> paNoError) then begin
- Log.CriticalError('Portaudio.InitializeRecord: ' + Pa_GetErrorText(err));
- //Log.LogError('Portaudio.InitializeRecord: ' + Pa_GetErrorText(err));
- // result := false;
+ Log.LogError('Portaudio.InitializeRecord: ' + Pa_GetErrorText(err));
Exit;
end;
-
+ writeln('1');
apiIndex := GetPreferredApiIndex();
apiInfo := Pa_GetHostApiInfo(apiIndex);
SC := 0;
+ writeln('2');
// init array-size to max. input-devices count
- SetLength(AudioInputProcessor.SoundCard, apiInfo^.deviceCount); // fix deviceCountL
- for i:= 0 to High(AudioInputProcessor.SoundCard) do
+ SetLength(AudioInputProcessor.Device, apiInfo^.deviceCount);
+ for i:= 0 to High(AudioInputProcessor.Device) do
begin
+ writeln('25');
// convert API-specific device-index to global index
deviceIndex := Pa_HostApiDeviceIndexToDeviceIndex(apiIndex, i);
deviceInfo := Pa_GetDeviceInfo(deviceIndex);
@@ -178,14 +218,13 @@ begin
if(deviceInfo^.maxInputChannels <= 0) then
continue;
- // TODO: free object on termination
- paSoundCard := TPortaudioSoundCard.Create();
- AudioInputProcessor.SoundCard[SC] := paSoundCard;
+ paDevice := TPortaudioInputDevice.Create();
+ AudioInputProcessor.Device[SC] := paDevice;
// retrieve device-name
deviceName := deviceInfo^.name;
- paSoundCard.Description := deviceName;
- paSoundCard.DeviceIndex := deviceIndex;
+ paDevice.Description := deviceName;
+ paDevice.PaDeviceIndex := deviceIndex;
// setup desired input parameters
with inputParams do begin
@@ -196,29 +235,32 @@ begin
hostApiSpecificStreamInfo := nil;
end;
+ paDevice.SampleRate := captureFreq;
+
// check if device supports our input-format
- err := Pa_IsFormatSupported(@inputParams, nil, sampleRate);
+ err := Pa_IsFormatSupported(@inputParams, nil, paDevice.SampleRate);
if(err <> 0) then begin
// format not supported -> skip
errMsg := Pa_GetErrorText(err);
Log.LogError('Portaudio.InitializeRecord, device: "'+ deviceName +'" '
+ '('+ errMsg +')');
- paSoundCard.Free();
+ paDevice.Free();
continue;
end;
// TODO: retry with mono if stereo is not supported
// TODO: retry with input-latency set to 20ms (defaultLowInputLatency might
// not be set correctly in OSS)
+ // use TPaDeviceInfo.defaultSampleRate
- err := Pa_OpenStream(stream, @inputParams, nil, sampleRate,
+ err := Pa_OpenStream(stream, @inputParams, nil, paDevice.SampleRate,
paFramesPerBufferUnspecified, paNoFlag, @MicrophoneCallback, nil);
if(err <> paNoError) then begin
// unable to open device -> skip
errMsg := Pa_GetErrorText(err);
Log.LogError('Portaudio.InitializeRecord, device: "'+ deviceName +'" '
+ '('+ errMsg +')');
- paSoundCard.Free();
+ paDevice.Free();
continue;
end;
@@ -229,14 +271,14 @@ begin
mixer := Px_OpenMixer(stream, 0);
// get input count
- inputCnt := Px_GetNumInputSources(mixer);
- SetLength(paSoundCard.Input, inputCnt);
+ sourceCnt := Px_GetNumInputSources(mixer);
+ SetLength(paDevice.Source, sourceCnt);
// get input names
- for SCI := 0 to inputCnt-1 do
+ for SCI := 0 to sourceCnt-1 do
begin
- inputName := Px_GetInputSourceName(mixer, SCI);
- paSoundCard.Input[SCI].Name := inputName;
+ sourceName := Px_GetInputSourceName(mixer, SCI);
+ paDevice.Source[SCI].Name := sourceName;
end;
Px_CloseMixer(mixer);
@@ -247,80 +289,46 @@ begin
// TODO: check if callback was called (this problem may occur on some devices)
//Pa_StopStream(stream);
- Pa_CloseStream(stream);
-
// create a standard input source
- SetLength(paSoundCard.Input, 1);
- paSoundCard.Input[0].Name := 'Standard';
+ SetLength(paDevice.Source, 1);
+ paDevice.Source[0].Name := 'Standard';
{$ENDIF}
+ // close test-stream
+ Pa_CloseStream(stream);
+
// use default input source
- paSoundCard.InputSelected := 0;
+ paDevice.SourceSelected := 0;
Inc(SC);
end;
+ writeln('3');
// adjust size to actual input-device count
- SetLength(AudioInputProcessor.SoundCard, SC);
+ SetLength(AudioInputProcessor.Device, SC);
Log.LogStatus('#Soundcards: ' + inttostr(SC), 'Portaudio');
{
SoundCard[SC].InputSelected := Mic[Device];
}
+ result := true;
end;
-// TODO: code is used by all IAudioInput implementors
-// -> move to a common superclass (TAudioInput_Generic?)
-procedure TAudioInput_Portaudio.CaptureStart;
-var
- S: integer;
- SC: integer;
- PlayerLeft, PlayerRight: integer;
- CaptureSoundLeft, CaptureSoundRight: TSound;
-begin
- for S := 0 to High(AudioInputProcessor.Sound) do
- AudioInputProcessor.Sound[S].BufferLong[0].Clear;
-
- for SC := 0 to High(Ini.CardList) do begin
- PlayerLeft := Ini.CardList[SC].ChannelL-1;
- PlayerRight := Ini.CardList[SC].ChannelR-1;
- if PlayerLeft >= PlayersPlay then PlayerLeft := -1;
- if PlayerRight >= PlayersPlay then PlayerRight := -1;
- if (PlayerLeft > -1) or (PlayerRight > -1) then begin
- if (PlayerLeft > -1) then
- CaptureSoundLeft := AudioInputProcessor.Sound[PlayerLeft]
- else
- CaptureSoundLeft := nil;
- if (PlayerRight > -1) then
- CaptureSoundRight := AudioInputProcessor.Sound[PlayerRight]
- else
- CaptureSoundRight := nil;
-
- CaptureCard(SC, CaptureSoundLeft, CaptureSoundRight);
- end;
- end;
-end;
-
-// TODO: code is used by all IAudioInput implementors
-// -> move to a common superclass (TAudioInput_Generic?)
-procedure TAudioInput_Portaudio.CaptureStop;
+destructor TAudioInput_Portaudio.Destroy;
var
- SC: integer;
- PlayerLeft: integer;
- PlayerRight: integer;
+ i: integer;
+ paSoundCard: TPortaudioInputDevice;
begin
-
- for SC := 0 to High(Ini.CardList) do begin
- PlayerLeft := Ini.CardList[SC].ChannelL-1;
- PlayerRight := Ini.CardList[SC].ChannelR-1;
- if PlayerLeft >= PlayersPlay then PlayerLeft := -1;
- if PlayerRight >= PlayersPlay then PlayerRight := -1;
- if (PlayerLeft > -1) or (PlayerRight > -1) then
- StopCard(SC);
+ Pa_Terminate();
+ for i := 0 to High(AudioInputProcessor.Device) do
+ begin
+ AudioInputProcessor.Device[i].Free();
end;
+ AudioInputProcessor.Device := nil;
+ inherited Destroy;
end;
{*
@@ -334,81 +342,6 @@ begin
result := paContinue;
end;
-{*
- * Start input-capturing on Soundcard specified by Card.
- * Params:
- * Card - soundcard index in Recording.SoundCard array
- * CaptureSoundLeft - sound(-buffer) used for left channel capture data
- * CaptureSoundRight - sound(-buffer) used for right channel capture data
- *}
-procedure TAudioInput_Portaudio.CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound);
-var
- Error: TPaError;
- ErrorMsg: string;
- inputParams: TPaStreamParameters;
- deviceInfo: PPaDeviceInfo;
- stream: PPaStream;
- paSoundCard: TPortaudioSoundCard;
-begin
- paSoundCard := TPortaudioSoundCard(AudioInputProcessor.SoundCard[Card]);
- paSoundCard.CaptureSoundLeft := CaptureSoundLeft;
- paSoundCard.CaptureSoundRight := CaptureSoundRight;
-
- // get input latency info
- deviceInfo := Pa_GetDeviceInfo(paSoundCard.DeviceIndex);
-
- // set input stream parameters
- with inputParams do begin
- device := paSoundCard.DeviceIndex;
- channelCount := 2;
- sampleFormat := paInt16;
- suggestedLatency := deviceInfo^.defaultLowInputLatency;
- hostApiSpecificStreamInfo := nil;
- end;
-
- Log.LogStatus(inttostr(paSoundCard.DeviceIndex), 'Portaudio');
- Log.LogStatus(floattostr(deviceInfo^.defaultLowInputLatency), 'Portaudio');
-
- // open input stream
- Error := Pa_OpenStream(stream, @inputParams, nil, sampleRate,
- paFramesPerBufferUnspecified, paNoFlag,
- @MicrophoneCallback, Pointer(paSoundCard));
- if(Error <> paNoError) then begin
- ErrorMsg := Pa_GetErrorText(Error);
- Log.CriticalError('TAudio_Portaudio.CaptureCard('+ IntToStr(Card) +'): Error opening stream: ' + ErrorMsg);
- //Halt;
- end;
-
- paSoundCard.RecordStream := stream;
-
- // start capture
- Error := Pa_StartStream(stream);
- if(Error <> paNoError) then begin
- Pa_CloseStream(stream);
- ErrorMsg := Pa_GetErrorText(Error);
- Log.CriticalError('TAudio_Portaudio.CaptureCard('+ IntToStr(Card) +'): Error starting stream: ' + ErrorMsg);
- //Halt;
- end;
-end;
-
-{*
- * Stop input-capturing on Soundcard specified by Card.
- * Params:
- * Card - soundcard index in Recording.SoundCard array
- *}
-procedure TAudioInput_Portaudio.StopCard(Card: byte);
-var
- stream: PPaStream;
- paSoundCard: TPortaudioSoundCard;
-begin
- paSoundCard := TPortaudioSoundCard(AudioInputProcessor.SoundCard[Card]);
- stream := paSoundCard.RecordStream;
- if(stream <> nil) then begin
- Pa_StopStream(stream);
- Pa_CloseStream(stream);
- end;
-end;
-
initialization
singleton_AudioInputPortaudio := TAudioInput_Portaudio.create();
diff --git a/Game/Code/Classes/UAudioPlayback_Bass.pas b/Game/Code/Classes/UAudioPlayback_Bass.pas
index 87ff183d..266a5ec3 100644
--- a/Game/Code/Classes/UAudioPlayback_Bass.pas
+++ b/Game/Code/Classes/UAudioPlayback_Bass.pas
@@ -9,35 +9,31 @@ interface
{$I switches.inc}
-uses Classes,
- {$IFDEF win32}
- windows,
- {$ENDIF}
- SysUtils,
- bass,
- ULog,
- UMusic;
+uses
+ Classes,
+ SysUtils,
+ UMusic;
implementation
uses
- {$IFDEF LAZARUS}
- lclintf,
- {$ENDIF}
- URecord,
- UIni,
- UMain,
- UCommon,
- UThemes;
+ UIni,
+ UMain,
+ ULog,
+ UAudioCore_Bass,
+ bass;
type
- TBassOutputStream = class(TAudioPlaybackStream)
+ TBassPlaybackStream = class(TAudioPlaybackStream)
private
Handle: HSTREAM;
+ Loop: boolean;
public
constructor Create(); overload;
constructor Create(stream: HSTREAM); overload;
+ procedure Reset();
+
procedure Play(); override;
procedure Pause(); override;
procedure Stop(); override;
@@ -46,41 +42,33 @@ type
procedure SetLoop(Enabled: boolean); override;
function GetLength(): real; override;
function GetStatus(): TStreamStatus; override;
+ function GetVolume(): integer; override;
+ procedure SetVolume(volume: integer); override;
+
+ function GetPosition: real;
+ procedure SetPosition(Time: real);
+
+ function IsLoaded(): boolean;
end;
type
TAudioPlayback_Bass = class( TInterfacedObject, IAudioPlayback)
private
- MusicStream: HSTREAM;
-
- StartSoundStream: HSTREAM;
- BackSoundStream: HSTREAM;
- SwooshSoundStream: HSTREAM;
- ChangeSoundStream: HSTREAM;
- OptionSoundStream: HSTREAM;
- ClickSoundStream: HSTREAM;
- DrumSoundStream: HSTREAM;
- HihatSoundStream: HSTREAM;
- ClapSoundStream: HSTREAM;
- ShuffleSoundStream: HSTREAM;
-
- //Custom Sounds
- CustomSounds: array of TCustomSoundEntry;
- Loaded: boolean;
- Loop: boolean;
+ MusicStream: TBassPlaybackStream;
+ function Load(Filename: string): TBassPlaybackStream;
public
function GetName: String;
{IAudioOutput interface}
- procedure InitializePlayback;
+ function InitializePlayback(): boolean;
procedure SetVolume(Volume: integer);
procedure SetMusicVolume(Volume: integer);
procedure SetLoop(Enabled: boolean);
- function Open(Name: string): boolean; // true if succeed
-
+ function Open(Filename: string): boolean; // true if succeed
+
procedure Rewind;
procedure Play;
procedure Pause; //Pause Mod
@@ -91,79 +79,106 @@ type
function GetPosition: real;
procedure SetPosition(Time: real);
- procedure PlayStart;
- procedure PlayBack;
- procedure PlaySwoosh;
- procedure PlayChange;
- procedure PlayOption;
- procedure PlayClick;
- procedure PlayDrum;
- procedure PlayHihat;
- procedure PlayClap;
- procedure PlayShuffle;
- procedure StopShuffle;
-
- function LoadSoundFromFile(var stream: HSTREAM; Name: string): boolean;
-
//Equalizer
- function GetFFTData: TFFTData;
+ procedure GetFFTData(var data: TFFTData);
// Interface for Visualizer
function GetPCMData(var data: TPCMData): Cardinal;
- //Custom Sounds
- function LoadCustomSound(const Filename: String): Cardinal;
- procedure PlayCustomSound(const Index: Cardinal );
+ // Sounds
+ function OpenSound(const Filename: String): TAudioPlaybackStream;
+ procedure PlaySound(stream: TAudioPlaybackStream);
+ procedure StopSound(stream: TAudioPlaybackStream);
end;
var
singleton_AudioPlaybackBass : IAudioPlayback;
-constructor TBassOutputStream.Create();
+constructor TBassPlaybackStream.Create();
begin
inherited;
+ Reset();
end;
-constructor TBassOutputStream.Create(stream: HSTREAM);
+constructor TBassPlaybackStream.Create(stream: HSTREAM);
begin
Create();
Handle := stream;
end;
-procedure TBassOutputStream.Play();
+procedure TBassPlaybackStream.Reset();
+begin
+ Loop := false;
+ if (Handle <> 0) then
+ Bass_StreamFree(Handle);
+ Handle := 0;
+end;
+
+procedure TBassPlaybackStream.Play();
begin
- BASS_ChannelPlay(Handle, True);
+ BASS_ChannelPlay(Handle, Loop);
end;
-procedure TBassOutputStream.Pause();
+procedure TBassPlaybackStream.Pause();
begin
- //if (Loaded) then
BASS_ChannelPause(Handle);
end;
-procedure TBassOutputStream.Stop();
+procedure TBassPlaybackStream.Stop();
begin
BASS_ChannelStop(Handle);
end;
-procedure TBassOutputStream.Close();
+procedure TBassPlaybackStream.Close();
begin
- Bass_StreamFree(Handle);
+ Reset();
end;
-function TBassOutputStream.GetLoop(): boolean;
+function TBassPlaybackStream.GetVolume(): integer;
begin
- // TODO
- result := false;
+ Result := 0;
+ BASS_ChannelSetAttributes(Handle, PInteger(nil)^, Result, PInteger(nil)^);
+end;
+
+procedure TBassPlaybackStream.SetVolume(volume: integer);
+begin
+ // clamp volume
+ if volume < 0 then
+ volume := 0;
+ if volume > 100 then
+ volume := 100;
+ // set volume
+ BASS_ChannelSetAttributes(Handle, -1, volume, -101);
end;
-procedure TBassOutputStream.SetLoop(Enabled: boolean);
+function TBassPlaybackStream.GetPosition: real;
+var
+ bytes: integer;
+begin
+ bytes := BASS_ChannelGetPosition(Handle);
+ Result := BASS_ChannelBytes2Seconds(Handle, bytes);
+end;
+
+procedure TBassPlaybackStream.SetPosition(Time: real);
+var
+ bytes: integer;
begin
- // TODO
+ bytes := BASS_ChannelSeconds2Bytes(Handle, Time);
+ BASS_ChannelSetPosition(Handle, bytes);
end;
-function TBassOutputStream.GetLength(): real;
+function TBassPlaybackStream.GetLoop(): boolean;
+begin
+ result := Loop;
+end;
+
+procedure TBassPlaybackStream.SetLoop(Enabled: boolean);
+begin
+ Loop := Enabled;
+end;
+
+function TBassPlaybackStream.GetLength(): real;
var
bytes: integer;
begin
@@ -171,37 +186,45 @@ begin
Result := BASS_ChannelBytes2Seconds(Handle, bytes);
end;
-function TBassOutputStream.GetStatus(): TStreamStatus;
+function TBassPlaybackStream.GetStatus(): TStreamStatus;
var
state: DWORD;
begin
state := BASS_ChannelIsActive(Handle);
case state of
BASS_ACTIVE_PLAYING:
- result := sPlaying;
+ result := ssPlaying;
BASS_ACTIVE_PAUSED:
- result := sPaused;
+ result := ssPaused;
+ BASS_ACTIVE_STALLED:
+ result := ssBlocked;
+ BASS_ACTIVE_STOPPED:
+ result := ssStopped;
else
- result := sStopped;
+ result := ssUnknown;
end;
end;
+function TBassPlaybackStream.IsLoaded(): boolean;
+begin
+ Result := (Handle <> 0);
+end;
+
function TAudioPlayback_Bass.GetName: String;
begin
result := 'BASS_Playback';
end;
-procedure TAudioPlayback_Bass.InitializePlayback;
+function TAudioPlayback_Bass.InitializePlayback(): boolean;
var
Pet: integer;
S: integer;
begin
-// Log.BenchmarkStart(4);
-// Log.LogStatus('Initializing Playback Subsystem', 'Music Initialize');
+ result := false;
- Loaded := false;
- Loop := false;
+ //Log.BenchmarkStart(4);
+ //Log.LogStatus('Initializing Playback Subsystem', 'Music Initialize');
if not BASS_Init(1, 44100, 0, 0, nil) then
begin
@@ -209,30 +232,32 @@ begin
Exit;
end;
-// Log.BenchmarkEnd(4); Log.LogBenchmark('--> Bass Init', 4);
+ //Log.BenchmarkEnd(4); Log.LogBenchmark('--> Bass Init', 4);
// config playing buffer
-// BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 10);
-// BASS_SetConfig(BASS_CONFIG_BUFFER, 100);
-
-// Log.LogStatus('Loading Sounds', 'Music Initialize');
+ //BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 10);
+ //BASS_SetConfig(BASS_CONFIG_BUFFER, 100);
-// Log.BenchmarkStart(4);
- LoadSoundFromFile(StartSoundStream, SoundPath + 'Common Start.mp3');
- LoadSoundFromFile(BackSoundStream, SoundPath + 'Common Back.mp3');
- LoadSoundFromFile(SwooshSoundStream, SoundPath + 'menu swoosh.mp3');
- LoadSoundFromFile(ChangeSoundStream, SoundPath + 'select music change music 50.mp3');
- LoadSoundFromFile(OptionSoundStream, SoundPath + 'option change col.mp3');
- LoadSoundFromFile(ClickSoundStream, SoundPath + 'rimshot022b.mp3');
+ result := true;
+end;
-// LoadSoundFromFile(DrumSoundStream, SoundPath + 'bassdrumhard076b.mp3');
-// LoadSoundFromFile(HihatSoundStream, SoundPath + 'hihatclosed068b.mp3');
-// LoadSoundFromFile(ClapSoundStream, SoundPath + 'claps050b.mp3');
+function TAudioPlayback_Bass.Load(Filename: string): TBassPlaybackStream;
+var
+ L: Integer;
+ stream: HSTREAM;
+begin
+ Result := nil;
-// LoadSoundFromFile(ShuffleSoundStream, SoundPath + 'Shuffle.mp3');
+ //Log.LogStatus('Loading Sound: "' + Filename + '"', 'LoadSoundFromFile');
+ stream := BASS_StreamCreateFile(False, pchar(Filename), 0, 0, 0);
+ if (stream = 0) then
+ begin
+ Log.LogError('Failed to open "' + Filename + '", ' +
+ TAudioCore_Bass.ErrorGetString(BASS_ErrorGetCode()), 'TAudioPlayback_Bass.Load');
+ Exit;
+ end;
-// Log.BenchmarkEnd(4);
-// Log.LogBenchmark('--> Loading Sounds', 4);
+ Result := TBassPlaybackStream.Create(stream);
end;
procedure TAudioPlayback_Bass.SetVolume(Volume: integer);
@@ -240,7 +265,6 @@ begin
//Old Sets Wave Volume
//BASS_SetVolume(Volume);
//New: Sets Volume only for this Application
-
BASS_SetConfig(BASS_CONFIG_GVOL_SAMPLE, Volume);
BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, Volume);
BASS_SetConfig(BASS_CONFIG_GVOL_MUSIC, Volume);
@@ -248,193 +272,102 @@ end;
procedure TAudioPlayback_Bass.SetMusicVolume(Volume: Integer);
begin
- //Max Volume Prevention
- if Volume > 100 then
- Volume := 100;
-
- if Volume < 0 then
- Volume := 0;
-
- //Set Volume
- BASS_ChannelSetAttributes (MusicStream, -1, Volume, -101);
+ if assigned(MusicStream) then
+ MusicStream.SetVolume(Volume);
end;
procedure TAudioPlayback_Bass.SetLoop(Enabled: boolean);
begin
- Loop := Enabled;
+ if assigned(MusicStream) then
+ MusicStream.Loop := Enabled;
end;
-function TAudioPlayback_Bass.Open(Name: string): boolean;
+function TAudioPlayback_Bass.Open(Filename: string): boolean;
+var
+ stream: HSTREAM;
begin
- Loaded := false;
- if FileExists(Name) then
- begin
- MusicStream := Bass_StreamCreateFile(false, pchar(Name), 0, 0, 0);
+ Result := false;
- Loaded := true;
- //Set Max Volume
- SetMusicVolume (100);
- end;
+ // free old MusicStream
+ if assigned(MusicStream) then
+ MusicStream.Free;
- Result := Loaded;
-end;
+ MusicStream := Load(Filename);
+ if not assigned(MusicStream) then
+ Exit;
-procedure TAudioPlayback_Bass.Rewind;
-begin
- if Loaded then begin
- end;
+ //Set Max Volume
+ SetMusicVolume(100);
+
+ Result := true;
end;
-procedure TAudioPlayback_Bass.SetPosition(Time: real);
-var
- bytes: integer;
+procedure TAudioPlayback_Bass.Rewind;
begin
- bytes := BASS_ChannelSeconds2Bytes(MusicStream, Time);
- BASS_ChannelSetPosition(MusicStream, bytes);
+ SetPosition(0);
end;
procedure TAudioPlayback_Bass.Play;
begin
- if Loaded then
- begin
- if Loop then
- BASS_ChannelPlay(MusicStream, True); // start from beginning... actually bass itself does not loop, nor does this TAudio_bass Class
-
- BASS_ChannelPlay(MusicStream, False); // for setting position before playing
- end;
+ if assigned(MusicStream) then
+ MusicStream.Play();
end;
-procedure TAudioPlayback_Bass.Pause; //Pause Mod
+procedure TAudioPlayback_Bass.Pause;
begin
- if Loaded then begin
- BASS_ChannelPause(MusicStream); // Pauses Song
- end;
+ if assigned(MusicStream) then
+ MusicStream.Pause();
end;
procedure TAudioPlayback_Bass.Stop;
begin
- Bass_ChannelStop(MusicStream);
+ if assigned(MusicStream) then
+ MusicStream.Stop();
end;
procedure TAudioPlayback_Bass.Close;
begin
- Bass_StreamFree(MusicStream);
+ if assigned(MusicStream) then
+ MusicStream.Close();
end;
function TAudioPlayback_Bass.Length: real;
var
bytes: integer;
begin
- bytes := BASS_ChannelGetLength(MusicStream);
- Result := BASS_ChannelBytes2Seconds(MusicStream, bytes);
-end;
-
-function TAudioPlayback_Bass.getPosition: real;
-var
- bytes: integer;
-begin
- bytes := BASS_ChannelGetPosition(MusicStream);
- Result := BASS_ChannelBytes2Seconds(MusicStream, bytes);
-end;
-
-function TAudioPlayback_Bass.Finished: boolean;
-begin
- Result := false;
-
- if BASS_ChannelIsActive(MusicStream) = BASS_ACTIVE_STOPPED then
- begin
- Result := true;
- end;
-end;
-
-procedure TAudioPlayback_Bass.PlayStart;
-begin
- BASS_ChannelPlay(StartSoundStream, True);
-end;
-
-procedure TAudioPlayback_Bass.PlayBack;
-begin
- BASS_ChannelPlay(BackSoundStream, True);// then
-end;
-
-procedure TAudioPlayback_Bass.PlaySwoosh;
-begin
- BASS_ChannelPlay(SwooshSoundStream, True);
-end;
-
-procedure TAudioPlayback_Bass.PlayChange;
-begin
- BASS_ChannelPlay(ChangeSoundStream, True);
-end;
-
-procedure TAudioPlayback_Bass.PlayOption;
-begin
- BASS_ChannelPlay(OptionSoundStream, True);
-end;
-
-procedure TAudioPlayback_Bass.PlayClick;
-begin
- BASS_ChannelPlay(ClickSoundStream, True);
-end;
-
-procedure TAudioPlayback_Bass.PlayDrum;
-begin
- BASS_ChannelPlay(DrumSoundStream, True);
-end;
-
-procedure TAudioPlayback_Bass.PlayHihat;
-begin
- BASS_ChannelPlay(HihatSoundStream, True);
-end;
-
-procedure TAudioPlayback_Bass.PlayClap;
-begin
- BASS_ChannelPlay(ClapSoundStream, True);
+ if assigned(MusicStream) then
+ Result := MusicStream.GetLength()
+ else
+ Result := -1;
end;
-procedure TAudioPlayback_Bass.PlayShuffle;
+function TAudioPlayback_Bass.GetPosition: real;
begin
- BASS_ChannelPlay(ShuffleSoundStream, True);
+ if assigned(MusicStream) then
+ Result := MusicStream.GetPosition()
+ else
+ Result := -1;
end;
-procedure TAudioPlayback_Bass.StopShuffle;
+procedure TAudioPlayback_Bass.SetPosition(Time: real);
begin
- BASS_ChannelStop(ShuffleSoundStream);
+ if assigned(MusicStream) then
+ MusicStream.SetPosition(Time);
end;
-function TAudioPlayback_Bass.LoadSoundFromFile(var stream: HSTREAM; Name: string): boolean;
-var
- L: Integer;
+function TAudioPlayback_Bass.Finished: boolean;
begin
- if FileExists(Name) then
- begin
- Log.LogStatus('Loading Sound: "' + Name + '"', 'LoadSoundFromFile');
- try
- stream := BASS_StreamCreateFile(False, pchar(Name), 0, 0, 0);
-
- //Add CustomSound
- L := High(CustomSounds) + 1;
- SetLength (CustomSounds, L + 1);
- CustomSounds[L].Filename := Name;
- CustomSounds[L].Stream := TBassOutputStream.Create(stream);
- except
- Log.LogError('Failed to open using BASS', 'LoadSoundFromFile');
- end;
- end
+ if assigned(MusicStream) then
+ Result := (MusicStream.GetStatus() = ssStopped)
else
- begin
- Log.LogError('Sound not found: "' + Name + '"', 'LoadSoundFromFile');
- exit;
- end;
+ Result := true;
end;
//Equalizer
-function TAudioPlayback_Bass.GetFFTData: TFFTData;
-var
- Data: TFFTData;
+procedure TAudioPlayback_Bass.GetFFTData(var data: TFFTData);
begin
//Get Channel Data Mono and 256 Values
- BASS_ChannelGetData(MusicStream, @Result, BASS_DATA_FFT512);
+ BASS_ChannelGetData(MusicStream.Handle, @data, BASS_DATA_FFT512);
end;
{*
@@ -447,23 +380,21 @@ var
nBytes: DWORD;
begin
//Get Channel Data Mono and 256 Values
- BASS_ChannelGetInfo(MusicStream, info);
- ZeroMemory(@data, sizeof(TPCMData));
+ BASS_ChannelGetInfo(MusicStream.Handle, info);
+ FillChar(data, sizeof(TPCMData), 0);
if (info.chans = 1) then
begin
// mono file -> add stereo channel
- {
- nBytes := BASS_ChannelGetData(Bass, @data[0], samples*sizeof(Smallint));
+ nBytes := 0;//BASS_ChannelGetData(Bass, @data[0], samples*sizeof(Smallint));
// interleave data
//CopyMemory(@data[1], @data[0], samples*sizeof(Smallint));
- }
result := 0;
end
else
begin
// stereo file
- nBytes := BASS_ChannelGetData(MusicStream, @data, sizeof(TPCMData));
+ nBytes := BASS_ChannelGetData(MusicStream.Handle, @data, sizeof(TPCMData));
end;
if(nBytes <= 0) then
result := 0
@@ -471,36 +402,21 @@ begin
result := nBytes div sizeof(TPCMStereoSample);
end;
-function TAudioPlayback_Bass.LoadCustomSound(const Filename: String): Cardinal;
-var
- S: hStream;
- I: Integer;
- F: String;
+function TAudioPlayback_Bass.OpenSound(const Filename: string): TAudioPlaybackStream;
begin
- //Search for Sound in already loaded Sounds
- F := UpperCase(SoundPath + FileName);
- For I := 0 to High(CustomSounds) do
- begin
- if (UpperCase(CustomSounds[I].Filename) = F) then
- begin
- Result := I;
- Exit;
- end;
- end;
+ result := Load(Filename);
+end;
- if LoadSoundFromFile(S, SoundPath + Filename) then
- Result := High(CustomSounds)
- else
- Result := 0;
+procedure TAudioPlayback_Bass.PlaySound(stream: TAudioPlaybackStream);
+begin
+ if assigned(stream) then
+ stream.Play();
end;
-procedure TAudioPlayback_Bass.PlayCustomSound(const Index: Cardinal );
+procedure TAudioPlayback_Bass.StopSound(stream: TAudioPlaybackStream);
begin
- if Index <= High(CustomSounds) then
- with CustomSounds[Index].Stream as TBassOutputStream do
- begin
- BASS_ChannelPlay(Handle, True);
- end;
+ if assigned(stream) then
+ stream.Stop();
end;
diff --git a/Game/Code/Classes/UAudioPlayback_Portaudio.pas b/Game/Code/Classes/UAudioPlayback_Portaudio.pas
index 5f4a8cde..2743fa86 100644
--- a/Game/Code/Classes/UAudioPlayback_Portaudio.pas
+++ b/Game/Code/Classes/UAudioPlayback_Portaudio.pas
@@ -9,35 +9,40 @@ interface
{$I switches.inc}
-uses Classes,
- SysUtils,
- UMusic;
+uses
+ Classes,
+ SysUtils,
+ UMusic;
implementation
uses
- {$IFDEF LAZARUS}
- lclintf,
- {$ifndef win32}
- libc,
- {$endif}
- {$ENDIF}
- sdl,
- portaudio,
- ULog,
- UIni,
- UMain;
+ {$IFNDEF Win32}
+ libc,
+ {$ENDIF}
+ sdl,
+ portaudio,
+ ULog,
+ UIni,
+ UMain;
type
TPortaudioPlaybackStream = class(TAudioPlaybackStream)
private
- status: TStreamStatus;
- Loaded: boolean;
+ Status: TStreamStatus;
Loop: boolean;
+
+ _volume: integer;
+
+ procedure Reset();
public
- decodeStream: TAudioDecodeStream;
+ DecodeStream: TAudioDecodeStream;
+
+ constructor Create();
+ destructor Destroy(); override;
+
+ function SetDecodeStream(decodeStream: TAudioDecodeStream): boolean;
- constructor Create(decodeStream: TAudioDecodeStream);
procedure Play(); override;
procedure Pause(); override;
procedure Stop(); override;
@@ -49,6 +54,9 @@ type
function IsLoaded(): boolean;
+ function GetVolume(): integer; override;
+ procedure SetVolume(volume: integer); override;
+
// functions delegated to the decode stream
function GetPosition: real;
procedure SetPosition(Time: real);
@@ -60,12 +68,23 @@ type
private
activeStreams: TList;
mixerBuffer: PChar;
+ internalLock: PSDL_Mutex;
+
+ _volume: integer;
+
+ procedure Lock(); inline;
+ procedure Unlock(); inline;
+
+ function GetVolume(): integer;
+ procedure SetVolume(volume: integer);
public
constructor Create();
destructor Destroy(); override;
procedure AddStream(stream: TAudioPlaybackStream);
procedure RemoveStream(stream: TAudioPlaybackStream);
function ReadData(Buffer: PChar; BufSize: integer): integer;
+
+ property Volume: integer READ GetVolume WRITE SetVolume;
end;
type
@@ -73,36 +92,29 @@ type
private
MusicStream: TPortaudioPlaybackStream;
- StartSoundStream: TPortaudioPlaybackStream;
- BackSoundStream: TPortaudioPlaybackStream;
- SwooshSoundStream: TPortaudioPlaybackStream;
- ChangeSoundStream: TPortaudioPlaybackStream;
- OptionSoundStream: TPortaudioPlaybackStream;
- ClickSoundStream: TPortaudioPlaybackStream;
- DrumSoundStream: TPortaudioPlaybackStream;
- HihatSoundStream: TPortaudioPlaybackStream;
- ClapSoundStream: TPortaudioPlaybackStream;
- ShuffleSoundStream: TPortaudioPlaybackStream;
-
- //Custom Sounds
- CustomSounds: array of TCustomSoundEntry;
-
- mixerStream: TAudioMixerStream;
+ MixerStream: TAudioMixerStream;
paStream: PPaStream;
- public
- FrameSize: integer;
- function GetName: String;
+ FrameSize: integer;
function InitializePortaudio(): boolean;
function StartPortaudioStream(): boolean;
- procedure InitializePlayback();
+ function InitializeSDLAudio(): boolean;
+ function StartSDLAudioStream(): boolean;
+ procedure StopSDLAudioStream();
+ public
+ function GetName: String;
+
+ function InitializePlayback(): boolean;
+ destructor Destroy; override;
+
+ function Load(const Filename: String): TPortaudioPlaybackStream;
+
procedure SetVolume(Volume: integer);
procedure SetMusicVolume(Volume: integer);
procedure SetLoop(Enabled: boolean);
function Open(Filename: string): boolean; // true if succeed
- function Load(Filename: string): TPortaudioPlaybackStream;
procedure Rewind;
procedure SetPosition(Time: real);
procedure Play;
@@ -113,27 +125,17 @@ type
function Finished: boolean;
function Length: real;
function GetPosition: real;
- procedure PlayStart;
- procedure PlayBack;
- procedure PlaySwoosh;
- procedure PlayChange;
- procedure PlayOption;
- procedure PlayClick;
- procedure PlayDrum;
- procedure PlayHihat;
- procedure PlayClap;
- procedure PlayShuffle;
- procedure StopShuffle;
-
- //Equalizer
- function GetFFTData: TFFTData;
+
+ // Equalizer
+ procedure GetFFTData(var data: TFFTData);
// Interface for Visualizer
function GetPCMData(var data: TPCMData): Cardinal;
- //Custom Sounds
- function LoadCustomSound(const Filename: String): Cardinal;
- procedure PlayCustomSound(const Index: Cardinal );
+ // Sounds
+ function OpenSound(const Filename: String): TAudioPlaybackStream;
+ procedure PlaySound(stream: TAudioPlaybackStream);
+ procedure StopSound(stream: TAudioPlaybackStream);
end;
@@ -150,25 +152,59 @@ var
constructor TAudioMixerStream.Create();
begin
activeStreams := TList.Create;
+ internalLock := SDL_CreateMutex();
+ _volume := 100;
end;
destructor TAudioMixerStream.Destroy();
begin
- if (mixerBuffer <> nil) then
+ if assigned(mixerBuffer) then
Freemem(mixerBuffer);
activeStreams.Free;
+ SDL_DestroyMutex(internalLock);
+end;
+
+procedure TAudioMixerStream.Lock();
+begin
+ SDL_mutexP(internalLock);
+end;
+
+procedure TAudioMixerStream.Unlock();
+begin
+ SDL_mutexV(internalLock);
+end;
+
+function TAudioMixerStream.GetVolume(): integer;
+begin
+ Lock();
+ result := _volume;
+ Unlock();
+end;
+
+procedure TAudioMixerStream.SetVolume(volume: integer);
+begin
+ Lock();
+ _volume := volume;
+ Unlock();
end;
procedure TAudioMixerStream.AddStream(stream: TAudioPlaybackStream);
begin
+ if not assigned(stream) then
+ Exit;
+
+ Lock();
// check if stream is already in list to avoid duplicates
if (activeStreams.IndexOf(Pointer(stream)) = -1) then
activeStreams.Add(Pointer(stream));
+ Unlock();
end;
procedure TAudioMixerStream.RemoveStream(stream: TAudioPlaybackStream);
begin
+ Lock();
activeStreams.Remove(Pointer(stream));
+ Unlock();
end;
function TAudioMixerStream.ReadData(Buffer: PChar; BufSize: integer): integer;
@@ -176,79 +212,111 @@ var
i: integer;
size: integer;
stream: TPortaudioPlaybackStream;
+ appVolume: single;
begin
result := BufSize;
// zero target-buffer (silence)
FillChar(Buffer^, BufSize, 0);
- // resize mixer-buffer
+ // resize mixer-buffer if necessary
ReallocMem(mixerBuffer, BufSize);
- if (mixerBuffer = nil) then
+ if not assigned(mixerBuffer) then
Exit;
- writeln('Mix: ' + inttostr(activeStreams.Count));
+ Lock();
+
+ //writeln('Mix: ' + inttostr(activeStreams.Count));
+
+ // use _volume instead of Volume to prevent recursive locking
+ appVolume := _volume / 100 * SDL_MIX_MAXVOLUME;
for i := 0 to activeStreams.Count-1 do
begin
stream := TPortaudioPlaybackStream(activeStreams[i]);
- if (stream.GetStatus() = sPlaying) then
+ if (stream.GetStatus() = ssPlaying) then
begin
// fetch data from current stream
size := stream.ReadData(mixerBuffer, BufSize);
if (size > 0) then
begin
- SDL_MixAudio(PUInt8(Buffer), PUInt8(mixerBuffer), size, SDL_MIX_MAXVOLUME);
+ SDL_MixAudio(PUInt8(Buffer), PUInt8(mixerBuffer), size,
+ Trunc(appVolume * stream.Volume / 100));
end;
end;
end;
+
+ Unlock();
end;
{ TPortaudioPlaybackStream }
-constructor TPortaudioPlaybackStream.Create(decodeStream: TAudioDecodeStream);
+constructor TPortaudioPlaybackStream.Create();
begin
inherited Create();
- status := sStopped;
- if (decodeStream <> nil) then
- begin
- Self.decodeStream := decodeStream;
- Loaded := true;
- end;
+ Reset();
+end;
+
+destructor TPortaudioPlaybackStream.Destroy();
+begin
+ Close();
+ inherited Destroy();
+end;
+
+procedure TPortaudioPlaybackStream.Reset();
+begin
+ Status := ssStopped;
+ Loop := false;
+ DecodeStream := nil;
+ _volume := 0;
+end;
+
+function TPortaudioPlaybackStream.SetDecodeStream(decodeStream: TAudioDecodeStream): boolean;
+begin
+ result := false;
+
+ Reset();
+
+ if not assigned(decodeStream) then
+ Exit;
+ Self.DecodeStream := decodeStream;
+
+ _volume := 100;
+
+ result := true;
+end;
+
+procedure TPortaudioPlaybackStream.Close();
+begin
+ Reset();
end;
procedure TPortaudioPlaybackStream.Play();
begin
- if (status <> sPaused) then
+ if (status <> ssPaused) then
begin
// rewind
- decodeStream.Position := 0;
+ if assigned(DecodeStream) then
+ DecodeStream.Position := 0;
end;
- status := sPlaying;
- //mixerStream.AddStream(Self);
+ status := ssPlaying;
+ //MixerStream.AddStream(Self);
end;
procedure TPortaudioPlaybackStream.Pause();
begin
- status := sPaused;
+ status := ssPaused;
end;
procedure TPortaudioPlaybackStream.Stop();
begin
- status := sStopped;
-end;
-
-procedure TPortaudioPlaybackStream.Close();
-begin
- status := sStopped;
- Loaded := false;
- // TODO: cleanup decode-stream
+ status := ssStopped;
end;
function TPortaudioPlaybackStream.IsLoaded(): boolean;
begin
- result := Loaded;
+ result := assigned(DecodeStream);
end;
function TPortaudioPlaybackStream.GetLoop(): boolean;
@@ -263,7 +331,10 @@ end;
function TPortaudioPlaybackStream.GetLength(): real;
begin
- result := decodeStream.Length;
+ if assigned(DecodeStream) then
+ result := DecodeStream.Length
+ else
+ result := -1;
end;
function TPortaudioPlaybackStream.GetStatus(): TStreamStatus;
@@ -273,22 +344,47 @@ end;
function TPortaudioPlaybackStream.ReadData(Buffer: PChar; BufSize: integer): integer;
begin
- result := decodeStream.ReadData(Buffer, BufSize);
+ if not assigned(DecodeStream) then
+ begin
+ result := -1;
+ Exit;
+ end;
+ result := DecodeStream.ReadData(Buffer, BufSize);
// end-of-file reached -> stop playback
- if (decodeStream.EOF) then
+ if (DecodeStream.EOF) then
begin
- status := sStopped;
+ status := ssStopped;
end;
end;
function TPortaudioPlaybackStream.GetPosition: real;
begin
- result := decodeStream.Position;
+ if assigned(DecodeStream) then
+ result := DecodeStream.Position
+ else
+ result := -1;
end;
procedure TPortaudioPlaybackStream.SetPosition(Time: real);
begin
- decodeStream.Position := Time;
+ if assigned(DecodeStream) then
+ DecodeStream.Position := Time;
+end;
+
+function TPortaudioPlaybackStream.GetVolume(): integer;
+begin
+ result := _volume;
+end;
+
+procedure TPortaudioPlaybackStream.SetVolume(volume: integer);
+begin
+ // clamp volume
+ if (volume > 100) then
+ _volume := 100
+ else if (volume < 0) then
+ _volume := 0
+ else
+ _volume := volume;
end;
@@ -298,18 +394,26 @@ function AudioCallback(input: Pointer; output: Pointer; frameCount: Longword;
timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags;
userData: Pointer): Integer; cdecl;
var
- playback : TAudioPlayback_Portaudio;
- playbackStream : TPortaudioPlaybackStream;
- decodeStream : TAudioDecodeStream;
+ playback: TAudioPlayback_Portaudio;
begin
playback := TAudioPlayback_Portaudio(userData);
with playback do
begin
- mixerStream.ReadData(output, frameCount * FrameSize);
+ MixerStream.ReadData(output, frameCount * FrameSize);
end;
result := paContinue;
end;
+procedure SDLAudioCallback(userdata: Pointer; stream: PChar; len: integer); cdecl;
+var
+ playback: TAudioPlayback_Portaudio;
+begin
+ playback := TAudioPlayback_Portaudio(userdata);
+ with playback do
+ begin
+ //MixerStream.ReadData(stream, len);
+ end;
+end;
function TAudioPlayback_Portaudio.GetName: String;
begin
@@ -349,10 +453,11 @@ begin
device := paOutDevice;
channelCount := 2;
sampleFormat := paInt16;
- suggestedLatency := paOutDeviceInfo^.defaultLowOutputLatency;
+ suggestedLatency := paOutDeviceInfo^.defaultHighOutputLatency;
hostApiSpecificStreamInfo := nil;
end;
+ // set the size of one audio frame (2channel 16bit uint sample)
FrameSize := 2 * sizeof(Smallint);
err := Pa_OpenStream(paStream, nil, @paOutParams, 44100,
@@ -384,219 +489,208 @@ begin
result := true;
end;
-procedure TAudioPlayback_Portaudio.InitializePlayback;
+function TAudioPlayback_Portaudio.InitializeSDLAudio(): boolean;
+var
+ desiredAudioSpec, obtainedAudioSpec: TSDL_AudioSpec;
+ err: integer;
begin
- Log.LogStatus('InitializePlayback', 'UAudioPlayback_Portaudio');
+ result := false;
- InitializePortaudio();
- mixerStream := TAudioMixerStream.Create;
+ SDL_InitSubSystem(SDL_INIT_AUDIO);
- StartSoundStream := Load(SoundPath + 'Common start.mp3');
- BackSoundStream := Load(SoundPath + 'Common back.mp3');
- SwooshSoundStream := Load(SoundPath + 'menu swoosh.mp3');
- ChangeSoundStream := Load(SoundPath + 'select music change music 50.mp3');
- OptionSoundStream := Load(SoundPath + 'option change col.mp3');
- ClickSoundStream := Load(SoundPath + 'rimshot022b.mp3');
+ FillChar(desiredAudioSpec, sizeof(desiredAudioSpec), 0);
+ with desiredAudioSpec do
+ begin
+ freq := 44100;
+ format := AUDIO_S16SYS;
+ channels := 2;
+ samples := 1024; // latency: 23 ms
+ callback := @SDLAudioCallback;
+ userdata := Self;
+ end;
-// DrumSoundStream := Load(SoundPath + 'bassdrumhard076b.mp3');
-// HihatSoundStream := Load(SoundPath + 'hihatclosed068b.mp3');
-// ClapSoundStream := Load(SoundPath + 'claps050b.mp3');
+ // set the size of one audio frame (2channel 16bit uint sample)
+ FrameSize := 2 * sizeof(Smallint);
+
+ if(SDL_OpenAudio(@desiredAudioSpec, @obtainedAudioSpec) = -1) then
+ begin
+ Log.LogStatus('SDL_OpenAudio: ' + SDL_GetError(), 'UAudioPlayback_SDL');
+ exit;
+ end;
-// ShuffleSoundStream := Load(SoundPath + 'Shuffle.mp3');
+ Log.LogStatus('Opened audio device', 'UAudioPlayback_SDL');
- StartPortaudioStream();
+ result := true;
end;
+function TAudioPlayback_Portaudio.StartSDLAudioStream(): boolean;
+begin
+ SDL_PauseAudio(0);
+ result := true;
+end;
-procedure TAudioPlayback_Portaudio.SetVolume(Volume: integer);
+procedure TAudioPlayback_Portaudio.StopSDLAudioStream();
begin
- //New: Sets Volume only for this Application
-(*
- BASS_SetConfig(BASS_CONFIG_GVOL_SAMPLE, Volume);
- BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, Volume);
- BASS_SetConfig(BASS_CONFIG_GVOL_MUSIC, Volume);
-*)
+ SDL_CloseAudio();
end;
-procedure TAudioPlayback_Portaudio.SetMusicVolume(Volume: Integer);
+function TAudioPlayback_Portaudio.InitializePlayback: boolean;
begin
- //Max Volume Prevention
- if Volume > 100 then
- Volume := 100;
+ result := false;
- if Volume < 0 then
- Volume := 0;
+ //Log.LogStatus('InitializePlayback', 'UAudioPlayback_Portaudio');
- //Set Volume
-// BASS_ChannelSetAttributes (Bass, -1, Volume, -101);
+ //if(not InitializePortaudio()) then
+ if(not InitializeSDLAudio()) then
+ Exit;
+
+ MixerStream := TAudioMixerStream.Create;
+
+ //if(not StartPortaudioStream()) then;
+ if(not StartSDLAudioStream()) then
+ Exit;
+
+ result := true;
end;
-procedure TAudioPlayback_Portaudio.SetLoop(Enabled: boolean);
+destructor TAudioPlayback_Portaudio.Destroy;
begin
- if (MusicStream <> nil) then
- if (MusicStream.IsLoaded) then
- MusicStream.SetLoop(Enabled);
+ StopSDLAudioStream();
+
+ MixerStream.Free();
+ MusicStream.Free();
+
+ inherited Destroy();
end;
-function TAudioPlayback_Portaudio.Open(Filename: string): boolean;
+function TAudioPlayback_Portaudio.Load(const Filename: String): TPortaudioPlaybackStream;
var
decodeStream: TAudioDecodeStream;
+ playbackStream: TPortaudioPlaybackStream;
begin
- decodeStream := AudioDecoder.Open(Filename);
- MusicStream := TPortaudioPlaybackStream.Create(decodeStream);
- // FIXME: remove this line
- mixerStream.AddStream(MusicStream);
+ Result := nil;
- if(MusicStream.IsLoaded()) then
+ decodeStream := AudioDecoder.Open(Filename);
+ if not assigned(decodeStream) then
begin
- //Set Max Volume
- SetMusicVolume(100);
+ Log.LogStatus('LoadSoundFromFile: Sound not found "' + Filename + '"', 'UAudioPlayback_Portaudio');
+ Exit;
end;
- Result := MusicStream.IsLoaded();
-end;
-
-procedure TAudioPlayback_Portaudio.Rewind;
-begin
- SetPosition(0);
-end;
+ playbackStream := TPortaudioPlaybackStream.Create();
+ if (not playbackStream.SetDecodeStream(decodeStream)) then
+ Exit;
-procedure TAudioPlayback_Portaudio.SetPosition(Time: real);
-begin
- if (MusicStream.IsLoaded) then
- begin
- MusicStream.SetPosition(Time);
- end;
-end;
+ // FIXME: remove this line
+ MixerStream.AddStream(playbackStream);
-function TAudioPlayback_Portaudio.GetPosition: real;
-begin
- if (MusicStream.IsLoaded) then
- Result := MusicStream.GetPosition();
+ result := playbackStream;
end;
-function TAudioPlayback_Portaudio.Length: real;
+procedure TAudioPlayback_Portaudio.SetVolume(Volume: integer);
begin
- Result := 0;
- if assigned( MusicStream ) then
- if (MusicStream.IsLoaded) then
- begin
- Result := MusicStream.GetLength();
- end;
+ // sets volume only for this application
+ MixerStream.Volume := Volume;
end;
-procedure TAudioPlayback_Portaudio.Play;
+procedure TAudioPlayback_Portaudio.SetMusicVolume(Volume: Integer);
begin
- if (MusicStream <> nil) then
- if (MusicStream.IsLoaded) then
- begin
- if (MusicStream.GetLoop()) then
- begin
- end;
- // start from beginning...
- // actually bass itself does not loop, nor does this TAudio_FFMpeg Class
- MusicStream.Play();
- end;
+ if assigned(MusicStream) then
+ MusicStream.Volume := Volume;
end;
-procedure TAudioPlayback_Portaudio.Pause;
+procedure TAudioPlayback_Portaudio.SetLoop(Enabled: boolean);
begin
- if (MusicStream <> nil) then
- MusicStream.Pause();
+ if assigned(MusicStream) then
+ MusicStream.SetLoop(Enabled);
end;
-procedure TAudioPlayback_Portaudio.Stop;
+function TAudioPlayback_Portaudio.Open(Filename: string): boolean;
+var
+ decodeStream: TAudioDecodeStream;
begin
- if MusicStream <> nil then
- MusicStream.Stop();
-end;
+ Result := false;
-procedure TAudioPlayback_Portaudio.Close;
-begin
- if MusicStream <> nil then
- MusicStream.Close();
-end;
+ // free old MusicStream
+ MusicStream.Free();
-function TAudioPlayback_Portaudio.Finished: boolean;
-begin
- if MusicStream <> nil then
- Result := (MusicStream.GetStatus() = sStopped);
-end;
+ MusicStream := Load(Filename);
+ if not assigned(MusicStream) then
+ Exit;
-procedure TAudioPlayback_Portaudio.PlayStart;
-begin
- if StartSoundStream <> nil then
- StartSoundStream.Play();
-end;
+ //Set Max Volume
+ SetMusicVolume(100);
-procedure TAudioPlayback_Portaudio.PlayBack;
-begin
- if BackSoundStream <> nil then
- BackSoundStream.Play();
+ Result := true;
end;
-procedure TAudioPlayback_Portaudio.PlaySwoosh;
+procedure TAudioPlayback_Portaudio.Rewind;
begin
- if SwooshSoundStream <> nil then
- SwooshSoundStream.Play();
+ SetPosition(0);
end;
-procedure TAudioPlayback_Portaudio.PlayChange;
+procedure TAudioPlayback_Portaudio.SetPosition(Time: real);
begin
- if ChangeSoundStream <> nil then
- ChangeSoundStream.Play();
+ if assigned(MusicStream) then
+ MusicStream.SetPosition(Time);
end;
-procedure TAudioPlayback_Portaudio.PlayOption;
+function TAudioPlayback_Portaudio.GetPosition: real;
begin
- if OptionSoundStream <> nil then
- OptionSoundStream.Play();
+ if assigned(MusicStream) then
+ Result := MusicStream.GetPosition()
+ else
+ Result := -1;
end;
-procedure TAudioPlayback_Portaudio.PlayClick;
+function TAudioPlayback_Portaudio.Length: real;
begin
- if ClickSoundStream <> nil then
- ClickSoundStream.Play();
+ if assigned(MusicStream) then
+ Result := MusicStream.GetLength()
+ else
+ Result := -1;
end;
-procedure TAudioPlayback_Portaudio.PlayDrum;
+procedure TAudioPlayback_Portaudio.Play;
begin
- if DrumSoundStream <> nil then
- DrumSoundStream.Play();
+ if assigned(MusicStream) then
+ MusicStream.Play();
end;
-procedure TAudioPlayback_Portaudio.PlayHihat;
+procedure TAudioPlayback_Portaudio.Pause;
begin
- if HihatSoundStream <> nil then
- HihatSoundStream.Play();
+ if assigned(MusicStream) then
+ MusicStream.Pause();
end;
-procedure TAudioPlayback_Portaudio.PlayClap;
+procedure TAudioPlayback_Portaudio.Stop;
begin
- if ClapSoundStream <> nil then
- ClapSoundStream.Play();
+ if assigned(MusicStream) then
+ MusicStream.Stop();
end;
-procedure TAudioPlayback_Portaudio.PlayShuffle;
+procedure TAudioPlayback_Portaudio.Close;
begin
- if ShuffleSoundStream <> nil then
- ShuffleSoundStream.Play();
+ if assigned(MusicStream) then
+ begin
+ MixerStream.RemoveStream(MusicStream);
+ MusicStream.Close();
+ end;
end;
-procedure TAudioPlayback_Portaudio.StopShuffle;
+function TAudioPlayback_Portaudio.Finished: boolean;
begin
- if ShuffleSoundStream <> nil then
- ShuffleSoundStream.Stop();
+ if assigned(MusicStream) then
+ Result := (MusicStream.GetStatus() = ssStopped)
+ else
+ Result := true;
end;
//Equalizer
-function TAudioPlayback_Portaudio.GetFFTData: TFFTData;
-var
- data: TFFTData;
+procedure TAudioPlayback_Portaudio.GetFFTData(var data: TFFTData);
begin
//Get Channel Data Mono and 256 Values
// BASS_ChannelGetData(Bass, @Result, BASS_DATA_FFT512);
- result := data;
end;
// Interface for Visualizer
@@ -605,66 +699,24 @@ begin
result := 0;
end;
-function TAudioPlayback_Portaudio.Load(Filename: string): TPortaudioPlaybackStream;
-var
- decodeStream : TAudioDecodeStream;
- playbackStream : TPortaudioPlaybackStream;
- csIndex : integer;
+function TAudioPlayback_Portaudio.OpenSound(const Filename: String): TAudioPlaybackStream;
begin
- result := nil;
-
- decodeStream := AudioDecoder.Open(Filename);
- if (decodeStream = nil) then
- begin
- Log.LogStatus('LoadSoundFromFile: Sound not found "' + Filename + '"', 'UAudioPlayback_Portaudio');
- exit;
- end;
-
- playbackStream := TPortaudioPlaybackStream.Create(decodeStream);
- // FIXME: remove this line
- mixerStream.AddStream(playbackStream);
-
- //Add CustomSound
- csIndex := High(CustomSounds) + 1;
- SetLength(CustomSounds, csIndex + 1);
- CustomSounds[csIndex].Filename := Filename;
- CustomSounds[csIndex].Stream := playbackStream;
-
- result := playbackStream;
+ result := Load(Filename);
end;
-function TAudioPlayback_Portaudio.LoadCustomSound(const Filename: String): Cardinal;
-var
- S: TAudioPlaybackStream;
- I: Integer;
- F: String;
+procedure TAudioPlayback_Portaudio.PlaySound(stream: TAudioPlaybackStream);
begin
- //Search for Sound in already loaded Sounds
- F := UpperCase(SoundPath + FileName);
- For I := 0 to High(CustomSounds) do
- begin
- if (UpperCase(CustomSounds[I].Filename) = F) then
- begin
- Result := I;
- Exit;
- end;
- end;
-
- S := Load(SoundPath + Filename);
- if (S <> nil) then
- Result := High(CustomSounds)
- else
- Result := 0;
+ if assigned(stream) then
+ stream.Play();
end;
-procedure TAudioPlayback_Portaudio.PlayCustomSound(const Index: Cardinal );
+procedure TAudioPlayback_Portaudio.StopSound(stream: TAudioPlaybackStream);
begin
- if (Index <= High(CustomSounds)) then
- CustomSounds[Index].Stream.Play();
+ if assigned(stream) then
+ stream.Stop();
end;
-
initialization
singleton_AudioPlaybackPortaudio := TAudioPlayback_Portaudio.create();
AudioManager.add( singleton_AudioPlaybackPortaudio );
diff --git a/Game/Code/Classes/UDraw.pas b/Game/Code/Classes/UDraw.pas
index 662050dc..a81aa93b 100644
--- a/Game/Code/Classes/UDraw.pas
+++ b/Game/Code/Classes/UDraw.pas
@@ -161,21 +161,25 @@ end;
procedure SingDrawOscilloscope(X, Y, W, H: real; NrSound: integer);
var
- Pet : integer;
+ SampleIndex: integer;
+ Sound: TSound;
+ MaxX, MaxY: real;
begin;
-// Log.LogStatus('Oscilloscope', 'SingDraw');
+ Sound := AudioInputProcessor.Sound[NrSound];
+
+ // Log.LogStatus('Oscilloscope', 'SingDraw');
glColor3f(Skin_OscR, Skin_OscG, Skin_OscB);
{if (ParamStr(1) = '-black') or (ParamStr(1) = '-fsblack') then
glColor3f(1, 1, 1); }
- glBegin(GL_LINE_STRIP);
-
- glVertex2f(X, -AudioInputProcessor.Sound[NrSound].BufferArray[1] / $10000 * H + Y + H/2);
+ MaxX := W-1;
+ MaxY := (H-1) / 2;
- for Pet := 2 to AudioInputProcessor.Sound[NrSound].n div 1 do
+ glBegin(GL_LINE_STRIP);
+ for SampleIndex := 0 to High(Sound.BufferArray) do
begin
- glVertex2f( X + (Pet-1) * W / (AudioInputProcessor.Sound[NrSound].n - 1),
- -AudioInputProcessor.Sound[NrSound].BufferArray[Pet] / $10000 * H + Y + H/2 );
+ glVertex2f(X + MaxX * SampleIndex/High(Sound.BufferArray),
+ Y + MaxY * (1 - Sound.BufferArray[SampleIndex]/-Low(Smallint)));
end;
glEnd;
end;
diff --git a/Game/Code/Classes/UIni.pas b/Game/Code/Classes/UIni.pas
index 599bb8fa..4ac67cda 100644
--- a/Game/Code/Classes/UIni.pas
+++ b/Game/Code/Classes/UIni.pas
@@ -1,798 +1,801 @@
-unit UIni;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses IniFiles, ULog, SysUtils;
-
-type
- TIni = class
- Name: array[0..11] of string;
-
- // Templates for Names Mod
- NameTeam: array[0..2] of string;
- NameTemplate: array[0..11] of string;
-
- //Filename of the opened iniFile
- Filename: string;
-
- // Game
- Players: integer;
- Difficulty: integer;
- Language: integer;
- Tabs: integer;
- Tabs_at_startup:integer; //Tabs at Startup fix
- Sorting: integer;
- Debug: integer;
-
- // Graphics
- Screens: integer;
- Resolution: integer;
- Depth: integer;
- FullScreen: integer;
- TextureSize: integer;
- SingWindow: integer;
- Oscilloscope: integer;
- Spectrum: integer;
- Spectrograph: integer;
- MovieSize: integer;
-
- // Sound
- MicBoost: integer;
- ClickAssist: integer;
- BeatClick: integer;
- SavePlayback: integer;
- Threshold: integer;
-
- //Song Preview
- PreviewVolume: integer;
- PreviewFading: integer;
-
- // Lyrics
- LyricsFont: integer;
- LyricsEffect: integer;
- Solmization: integer;
-
- // Themes
- Theme: integer;
- SkinNo: integer;
- Color: integer;
-
- // Record
- Card: integer; // not saved in config.ini
-
- CardList: array of record
- Name: string;
- Input: integer;
- ChannelL: integer;
- ChannelR: integer;
- end;
-
- // Advanced
- LoadAnimation: integer;
- EffectSing: integer;
- ScreenFade: integer;
- AskbeforeDel: integer;
- OnSongClick: integer;
- LineBonus: integer;
- PartyPopup: integer;
-
- // Controller
- Joypad: integer;
-
- // Soundcards
- SoundCard: array[0..7, 1..2] of integer;
-
- // Devices
- LPT: integer;
-
- procedure Load;
- procedure Save;
- procedure SaveNames;
- procedure SaveLevel;
- end;
-
-
-var
- Ini: TIni;
- IResolution: array of string;
- ILanguage: array of string;
- ITheme: array of string;
- ISkin: array of string;
- ICard: array of string;
- IInput: array of string;
-
-const
- IPlayers: array[0..4] of string = ('1', '2', '3', '4', '6');
- IDifficulty: array[0..2] of string = ('Easy', 'Medium', 'Hard');
- ITabs: array[0..1] of string = ('Off', 'On');
-
- ISorting: array[0..7] of string = ('Edition', 'Genre', 'Language', 'Folder', 'Title', 'Artist', 'Title2', 'Artist2');
- sEdition = 0;
- sGenre = 1;
- sLanguage = 2;
- sFolder = 3;
- sTitle = 4;
- sArtist = 5;
- sTitle2 = 6;
- sArtist2 = 7;
-
- IDebug: array[0..1] of string = ('Off', 'On');
-
- IScreens: array[0..1] of string = ('1', '2');
- IFullScreen: array[0..1] of string = ('Off', 'On');
- IDepth: array[0..1] of string = ('16 bit', '32 bit');
- ITextureSize: array[0..2] of string = ('128', '256', '512');
- ISingWindow: array[0..1] of string = ('Small', 'Big');
-
- //SingBar Mod
- IOscilloscope: array[0..2] of string = ('Off', 'Osci', 'Bar');
- //IOscilloscope: array[0..1] of string = ('Off', 'On');
-
- ISpectrum: array[0..1] of string = ('Off', 'On');
- ISpectrograph: array[0..1] of string = ('Off', 'On');
- IMovieSize: array[0..2] of string = ('Half', 'Full [Vid]', 'Full [BG+Vid]');
-
- IMicBoost: array[0..3] of string = ('Off', '+6dB', '+12dB', '+18dB');
- IClickAssist: array[0..1] of string = ('Off', 'On');
- IBeatClick: array[0..1] of string = ('Off', 'On');
- ISavePlayback: array[0..1] of string = ('Off', 'On');
- IThreshold: array[0..3] of string = ('5%', '10%', '15%', '20%');
- //Song Preview
- IPreviewVolume: array[0..10] of string = ('Off', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%');
- IPreviewFading: array[0..5] of string = ('Off', '1 Sec', '2 Secs', '3 Secs', '4 Secs', '5 Secs');
-
-
- ILyricsFont: array[0..2] of string = ('Plain', 'OLine1', 'OLine2');
- ILyricsEffect: array[0..3] of string = ('Simple', 'Zoom', 'Slide', 'Ball');
- ISolmization: array[0..3] of string = ('Off', 'Euro', 'Jap', 'American');
-
- IColor: array[0..8] of string = ('Blue', 'Green', 'Pink', 'Red', 'Violet', 'Orange', 'Yellow', 'Brown', 'Black');
-
- // Advanced
- ILoadAnimation: array[0..1] of string = ('Off', 'On');
- IEffectSing: array[0..1] of string = ('Off', 'On');
- IScreenFade: array [0..1] of String =('Off', 'On');
- IAskbeforeDel: array[0..1] of string = ('Off', 'On');
- IOnSongClick: array[0..2] of string = ('Sing', 'Select Players', 'Open Menu');
- ILineBonus: array[0..2] of string = ('Off', 'At Score', 'At Notes');
- IPartyPopup: array[0..1] of string = ('Off', 'On');
-
- IJoypad: array[0..1] of string = ('Off', 'On');
- ILPT: array[0..2] of string = ('Off', 'LCD', 'Lights');
-
- IChannel: array[0..6] of string = ('Off', '1', '2', '3', '4', '5', '6');
-
-implementation
-
-uses //UFiles,
- UMain,
- SDL,
- ULanguage,
- UPlatform,
- USkins,
- URecord,
- UCommandLine;
-
-procedure TIni.Load;
-var
- IniFile: TMemIniFile;
- ThemeIni: TMemIniFile;
- Tekst: string;
- Pet: integer;
- B: boolean;
- I, I2, I3: integer;
- S: string;
- Modes: PPSDL_Rect;
- SR: TSearchRec; //Skin List Patch
-
- function GetFileName (S: String):String;
- begin
- //Result := copy (S,0,StrRScan (PChar(S),char('.'))+1);
- Result := copy (S,0,Pos ('.ini',S)-1);
- end;
-
-begin
- GamePath := Platform.GetGameUserPath;
-
- if (Params.ConfigFile <> '') then
- try
- IniFile := TMemIniFile.Create(Params.ConfigFile);
- except
- IniFile := TMemIniFile.Create(GamePath + 'config.ini');
- end
- else
- IniFile := TMemIniFile.Create(GamePath + 'config.ini');
-
-
- // Name
- for I := 0 to 11 do
- Ini.Name[I] := IniFile.ReadString('Name', 'P'+IntToStr(I+1), 'Player'+IntToStr(I+1));
-
-
- // Templates for Names Mod
- for I := 0 to 2 do
- Ini.NameTeam[I] := IniFile.ReadString('NameTeam', 'T'+IntToStr(I+1), 'Team'+IntToStr(I+1));
- for I := 0 to 11 do
- Ini.NameTemplate[I] := IniFile.ReadString('NameTemplate', 'Name'+IntToStr(I+1), 'Template'+IntToStr(I+1));
-
- // Players
- Tekst := IniFile.ReadString('Game', 'Players', IPlayers[0]);
- for Pet := 0 to High(IPlayers) do
- if Tekst = IPlayers[Pet] then Ini.Players := Pet;
-
- // Difficulty
- Tekst := IniFile.ReadString('Game', 'Difficulty', 'Easy');
- for Pet := 0 to High(IDifficulty) do
- if Tekst = IDifficulty[Pet] then Ini.Difficulty := Pet;
-
- // Language
- Tekst := IniFile.ReadString('Game', 'Language', 'English');
- for Pet := 0 to High(ILanguage) do
- if Tekst = ILanguage[Pet] then Ini.Language := Pet;
-
-// Language.ChangeLanguage(ILanguage[Ini.Language]);
-
- // Tabs
- Tekst := IniFile.ReadString('Game', 'Tabs', ITabs[0]);
- for Pet := 0 to High(ITabs) do
- if Tekst = ITabs[Pet] then Ini.Tabs := Pet;
-
- //Tabs at Startup fix
- Ini.Tabs_at_startup := Ini.Tabs;
-
- // Sorting
- Tekst := IniFile.ReadString('Game', 'Sorting', ISorting[0]);
- for Pet := 0 to High(ISorting) do
- if Tekst = ISorting[Pet] then Ini.Sorting := Pet;
-
- // Debug
- Tekst := IniFile.ReadString('Game', 'Debug', IDebug[0]);
- for Pet := 0 to High(IDebug) do
- if Tekst = IDebug[Pet] then Ini.Debug := Pet;
-
- //if Ini.Debug = 1 then SongPath := 'E:\UltraStar 03\Songs\';
-
- // Screens
- Tekst := IniFile.ReadString('Graphics', 'Screens', IScreens[0]);
- for Pet := 0 to High(IScreens) do
- if Tekst = IScreens[Pet] then Ini.Screens := Pet;
-
- // FullScreen
- Tekst := IniFile.ReadString('Graphics', 'FullScreen', 'On');
- for Pet := 0 to High(IFullScreen) do
- if Tekst = IFullScreen[Pet] then Ini.FullScreen := Pet;
-
-
- // Resolution
- SetLength(IResolution, 0);
-
- Modes := SDL_ListModes(nil, SDL_OPENGL or SDL_FULLSCREEN); // Check if there are any modes available
- while assigned( Modes^ ) do //this should solve the biggest wine problem | THANKS Linnex (11.11.07)
- begin
- SetLength(IResolution, Length(IResolution) + 1);
- IResolution[High(IResolution)] := IntToStr(Modes^.w) + 'x' + IntToStr(Modes^.h);
- Inc(Modes);
- end;
-
- // if no modes were set, then failback to 800x600
- // as per http://sourceforge.net/forum/message.php?msg_id=4544965
- // THANKS : linnex at users.sourceforge.net
- if Length(IResolution) < 1 then
- begin
- SetLength(IResolution, Length(IResolution) + 1);
- IResolution[High(IResolution)] := IntToStr(800) + 'x' + IntToStr(600);
- Log.LogStatus('SDL_ListModes Defaulted Res To : ' + IResolution[High(IResolution)] , 'Graphics - Resolutions');
-
- // Default to fullscreen OFF, in this case !
- Ini.FullScreen := 0;
- end;
-
- // reverse order
- for I := 0 to (Length(IResolution) div 2) - 1 do begin
- S := IResolution[I];
- IResolution[I] := IResolution[High(IResolution)-I];
- IResolution[High(IResolution)-I] := S;
- end;
-
- Tekst := IniFile.ReadString('Graphics', 'Resolution', '800x600');
- for Pet := 0 to High(IResolution) do
- if Tekst = IResolution[Pet] then Ini.Resolution := Pet;
-
-
- // Resolution
- Tekst := IniFile.ReadString('Graphics', 'Depth', '32 bit');
- for Pet := 0 to High(IDepth) do
- if Tekst = IDepth[Pet] then Ini.Depth := Pet;
-
- // Texture Size
- Tekst := IniFile.ReadString('Graphics', 'TextureSize', ITextureSize[1]);
- for Pet := 0 to High(ITextureSize) do
- if Tekst = ITextureSize[Pet] then Ini.TextureSize := Pet;
-
- // SingWindow
- Tekst := IniFile.ReadString('Graphics', 'SingWindow', 'Big');
- for Pet := 0 to High(ISingWindow) do
- if Tekst = ISingWindow[Pet] then Ini.SingWindow := Pet;
-
- // Oscilloscope
- Tekst := IniFile.ReadString('Graphics', 'Oscilloscope', 'Bar');
- for Pet := 0 to High(IOscilloscope) do
- if Tekst = IOscilloscope[Pet] then Ini.Oscilloscope := Pet;
-
- // Spectrum
- Tekst := IniFile.ReadString('Graphics', 'Spectrum', 'Off');
- for Pet := 0 to High(ISpectrum) do
- if Tekst = ISpectrum[Pet] then Ini.Spectrum := Pet;
-
- // Spectrograph
- Tekst := IniFile.ReadString('Graphics', 'Spectrograph', 'Off');
- for Pet := 0 to High(ISpectrograph) do
- if Tekst = ISpectrograph[Pet] then Ini.Spectrograph := Pet;
-
- // MovieSize
- Tekst := IniFile.ReadString('Graphics', 'MovieSize', IMovieSize[2]);
- for Pet := 0 to High(IMovieSize) do
- if Tekst = IMovieSize[Pet] then Ini.MovieSize := Pet;
-
- // MicBoost
- Tekst := IniFile.ReadString('Sound', 'MicBoost', 'Off');
- for Pet := 0 to High(IMicBoost) do
- if Tekst = IMicBoost[Pet] then Ini.MicBoost := Pet;
-
- // ClickAssist
- Tekst := IniFile.ReadString('Sound', 'ClickAssist', 'Off');
- for Pet := 0 to High(IClickAssist) do
- if Tekst = IClickAssist[Pet] then Ini.ClickAssist := Pet;
-
- // BeatClick
- Tekst := IniFile.ReadString('Sound', 'BeatClick', IBeatClick[0]);
- for Pet := 0 to High(IBeatClick) do
- if Tekst = IBeatClick[Pet] then Ini.BeatClick := Pet;
-
- // SavePlayback
- Tekst := IniFile.ReadString('Sound', 'SavePlayback', ISavePlayback[0]);
- for Pet := 0 to High(ISavePlayback) do
- if Tekst = ISavePlayback[Pet] then Ini.SavePlayback := Pet;
-
- // Threshold
- Tekst := IniFile.ReadString('Sound', 'Threshold', IThreshold[2]);
- for Pet := 0 to High(IThreshold) do
- if Tekst = IThreshold[Pet] then Ini.Threshold := Pet;
-
- //Song Preview
- Tekst := IniFile.ReadString('Sound', 'PreviewVolume', IPreviewVolume[7]);
- for Pet := 0 to High(IPreviewVolume) do
- if Tekst = IPreviewVolume[Pet] then Ini.PreviewVolume := Pet;
-
- Tekst := IniFile.ReadString('Sound', 'PreviewFading', IPreviewFading[1]);
- for Pet := 0 to High(IPreviewFading) do
- if Tekst = IPreviewFading[Pet] then Ini.PreviewFading := Pet;
-
- // Lyrics Font
- Tekst := IniFile.ReadString('Lyrics', 'LyricsFont', ILyricsFont[1]);
- for Pet := 0 to High(ILyricsFont) do
- if Tekst = ILyricsFont[Pet] then Ini.LyricsFont := Pet;
-
- // Lyrics Effect
- Tekst := IniFile.ReadString('Lyrics', 'LyricsEffect', ILyricsEffect[1]);
- for Pet := 0 to High(ILyricsEffect) do
- if Tekst = ILyricsEffect[Pet] then Ini.LyricsEffect := Pet;
-
- // Solmization
- Tekst := IniFile.ReadString('Lyrics', 'Solmization', ISolmization[0]);
- for Pet := 0 to High(ISolmization) do
- if Tekst = ISolmization[Pet] then Ini.Solmization := Pet;
-
- // Theme
-
- //Theme List Patch
-
- //I2 Saves the no of the Deluxe (Standard-) Theme
- I2 := 0;
- //I counts is the cur. Theme no
- I := 0;
-
- SetLength(ITheme, 0);
- writeln( 'Searching for Theme : '+ ThemePath + '*.ini' );
- FindFirst(ThemePath + '*.ini',faAnyFile,SR);
- Repeat
- writeln( SR.Name );
-
- //Read Themename from Theme
- ThemeIni := TMemIniFile.Create(SR.Name);
- Tekst := UpperCase(ThemeIni.ReadString('Theme','Name',GetFileName(SR.Name)));
- ThemeIni.Free;
-
- //if Deluxe Theme then save Themeno to I2
- if (Tekst = 'DELUXE') then
- I2 := I;
-
- //Search for Skins for this Theme
- for Pet := low(Skin.Skin) to high(Skin.Skin) do
- begin
- if UpperCase(Skin.Skin[Pet].Theme) = Tekst then
- begin
- SetLength(ITheme, Length(ITheme)+1);
- ITheme[High(ITheme)] := GetFileName(SR.Name);
- break;
- end;
- end;
-
- Inc(I);
- Until FindNext(SR) <> 0;
- FindClose(SR);
- //Theme List Patch End }
-
- //No Theme Found
- if (Length(ITheme)=0) then
- begin
- Log.CriticalError('Could not find any valid Themes.');
- end;
-
-
- Tekst := IniFile.ReadString('Themes', 'Theme', ITheme[I2]);
- Ini.Theme := 0;
- for Pet := 0 to High(ITheme) do
- if Uppercase(Tekst) = Uppercase(ITheme[Pet]) then Ini.Theme := Pet;
-
- // Skin
- Skin.onThemeChange;
- Ini.SkinNo := 0;
-
- Tekst := IniFile.ReadString('Themes', 'Skin', ISkin[0]);
- for Pet := 0 to High(ISkin) do
- if Tekst = ISkin[Pet] then Ini.SkinNo := Pet;
-
- // Color
- Tekst := IniFile.ReadString('Themes', 'Color', IColor[0]);
- for Pet := 0 to High(IColor) do
- if Tekst = IColor[Pet] then Ini.Color := Pet;
-
- // Record - load ini list
- SetLength(CardList, 0);
- I := 1;
- while (IniFile.ValueExists('Record', 'DeviceName' + IntToStr(I)) = true) do begin
- //Automatically Delete not Existing Sound Cards
- S := IniFile.ReadString('Record', 'DeviceName' + IntToStr(I), '');
- //{
- B := False;
- //Look for Soundcard
- for I2 := 0 to High(AudioInputProcessor.SoundCard) do
- begin
- if (S = Trim(AudioInputProcessor.SoundCard[I2].Description)) then
- begin
- B := True;
- Break;
- end;
- end;
-
- if B then
- begin //}
- I3 := Length(CardList);
- SetLength(CardList, I3+1);
- Ini.CardList[I3].Name := S;
- Ini.CardList[I3].Input := IniFile.ReadInteger('Record', 'Input' + IntToStr(I), 0);
- Ini.CardList[I3].ChannelL := IniFile.ReadInteger('Record', 'ChannelL' + IntToStr(I), 0);
- Ini.CardList[I3].ChannelR := IniFile.ReadInteger('Record', 'ChannelR' + IntToStr(I), 0);
- end;
- Inc(I);
- end;
-
- // Record - append detected soundcards
- for I := 0 to High(AudioInputProcessor.SoundCard) do
- begin
- B := False;
- For I2 := 0 to High(CardList) do
- begin //Search for Card in List
- if (CardList[I2].Name = Trim(AudioInputProcessor.SoundCard[I].Description)) then
- begin
- B := True;
- Break;
- end;
- end;
-
- //If not in List -> Add
- If not B then
- begin
- I3 := Length(CardList);
- SetLength(CardList, I3+1);
- CardList[I3].Name := Trim(AudioInputProcessor.SoundCard[I].Description);
- CardList[I3].Input := 0;
- CardList[I3].ChannelL := 0;
- CardList[I3].ChannelR := 0;
- // default for new users
- if (Length(CardList) = 1) then
- CardList[I].ChannelL := 1;
- end;
- end;
-
- //Advanced Settings
-
- // LoadAnimation
- Tekst := IniFile.ReadString('Advanced', 'LoadAnimation', 'On');
- for Pet := 0 to High(ILoadAnimation) do
- if Tekst = ILoadAnimation[Pet] then Ini.LoadAnimation := Pet;
-
- // ScreenFade
- Tekst := IniFile.ReadString('Advanced', 'ScreenFade', 'On');
- for Pet := 0 to High(IScreenFade) do
- if Tekst = IScreenFade[Pet] then Ini.ScreenFade := Pet;
-
- // EffectSing
- Tekst := IniFile.ReadString('Advanced', 'EffectSing', 'On');
- for Pet := 0 to High(IEffectSing) do
- if Tekst = IEffectSing[Pet] then Ini.EffectSing := Pet;
-
- // AskbeforeDel
- Tekst := IniFile.ReadString('Advanced', 'AskbeforeDel', 'On');
- for Pet := 0 to High(IAskbeforeDel) do
- if Tekst = IAskbeforeDel[Pet] then Ini.AskbeforeDel := Pet;
-
- // OnSongClick
- Tekst := IniFile.ReadString('Advanced', 'OnSongClick', 'Sing');
- for Pet := 0 to High(IOnSongClick) do
- if Tekst = IOnSongClick[Pet] then Ini.OnSongClick := Pet;
-
- // Linebonus
- Tekst := IniFile.ReadString('Advanced', 'LineBonus', 'At Score');
- for Pet := 0 to High(ILineBonus) do
- if Tekst = ILineBonus[Pet] then Ini.LineBonus := Pet;
-
- // PartyPopup
- Tekst := IniFile.ReadString('Advanced', 'PartyPopup', 'On');
- for Pet := 0 to High(IPartyPopup) do
- if Tekst = IPartyPopup[Pet] then Ini.PartyPopup := Pet;
-
-
- // Joypad
- Tekst := IniFile.ReadString('Controller', 'Joypad', IJoypad[0]);
- for Pet := 0 to High(IJoypad) do
- if Tekst = IJoypad[Pet] then Ini.Joypad := Pet;
-
- // LCD
- Tekst := IniFile.ReadString('Devices', 'LPT', ILPT[0]);
- for Pet := 0 to High(ILPT) do
- if Tekst = ILPT[Pet] then Ini.LPT := Pet;
-
-
- // SongPath
- if (Params.SongPath <> '') then
- SongPath := IncludeTrailingPathDelimiter(Params.SongPath)
- else
- SongPath := IncludeTrailingPathDelimiter(IniFile.ReadString('Path', 'Songs', SongPath));
-
- Filename := IniFile.FileName;
- IniFile.Free;
-end;
-
-procedure TIni.Save;
-var
- IniFile: TIniFile;
- Tekst: string;
- I: Integer;
- S: String;
-begin
- //if not (FileExists(GamePath + 'config.ini') and FileIsReadOnly(GamePath + 'config.ini')) then begin
- if not (FileExists(Filename) and FileIsReadOnly(Filename)) then begin
-
- IniFile := TIniFile.Create(Filename);
-
- // Players
- Tekst := IPlayers[Ini.Players];
- IniFile.WriteString('Game', 'Players', Tekst);
-
- // Difficulty
- Tekst := IDifficulty[Ini.Difficulty];
- IniFile.WriteString('Game', 'Difficulty', Tekst);
-
- // Language
- Tekst := ILanguage[Ini.Language];
- IniFile.WriteString('Game', 'Language', Tekst);
-
- // Tabs
- Tekst := ITabs[Ini.Tabs];
- IniFile.WriteString('Game', 'Tabs', Tekst);
-
- // Sorting
- Tekst := ISorting[Ini.Sorting];
- IniFile.WriteString('Game', 'Sorting', Tekst);
-
- // Debug
- Tekst := IDebug[Ini.Debug];
- IniFile.WriteString('Game', 'Debug', Tekst);
-
- // Screens
- Tekst := IScreens[Ini.Screens];
- IniFile.WriteString('Graphics', 'Screens', Tekst);
-
- // FullScreen
- Tekst := IFullScreen[Ini.FullScreen];
- IniFile.WriteString('Graphics', 'FullScreen', Tekst);
-
- // Resolution
- Tekst := IResolution[Ini.Resolution];
- IniFile.WriteString('Graphics', 'Resolution', Tekst);
-
- // Depth
- Tekst := IDepth[Ini.Depth];
- IniFile.WriteString('Graphics', 'Depth', Tekst);
-
- // Resolution
- Tekst := ITextureSize[Ini.TextureSize];
- IniFile.WriteString('Graphics', 'TextureSize', Tekst);
-
- // Sing Window
- Tekst := ISingWindow[Ini.SingWindow];
- IniFile.WriteString('Graphics', 'SingWindow', Tekst);
-
- // Oscilloscope
- Tekst := IOscilloscope[Ini.Oscilloscope];
- IniFile.WriteString('Graphics', 'Oscilloscope', Tekst);
-
- // Spectrum
- Tekst := ISpectrum[Ini.Spectrum];
- IniFile.WriteString('Graphics', 'Spectrum', Tekst);
-
- // Spectrograph
- Tekst := ISpectrograph[Ini.Spectrograph];
- IniFile.WriteString('Graphics', 'Spectrograph', Tekst);
-
- // Movie Size
- Tekst := IMovieSize[Ini.MovieSize];
- IniFile.WriteString('Graphics', 'MovieSize', Tekst);
-
- // MicBoost
- Tekst := IMicBoost[Ini.MicBoost];
- IniFile.WriteString('Sound', 'MicBoost', Tekst);
-
- // ClickAssist
- Tekst := IClickAssist[Ini.ClickAssist];
- IniFile.WriteString('Sound', 'ClickAssist', Tekst);
-
- // BeatClick
- Tekst := IBeatClick[Ini.BeatClick];
- IniFile.WriteString('Sound', 'BeatClick', Tekst);
-
- // Threshold
- Tekst := IThreshold[Ini.Threshold];
- IniFile.WriteString('Sound', 'Threshold', Tekst);
-
- // Song Preview
- Tekst := IPreviewVolume[Ini.PreviewVolume];
- IniFile.WriteString('Sound', 'PreviewVolume', Tekst);
-
- Tekst := IPreviewFading[Ini.PreviewFading];
- IniFile.WriteString('Sound', 'PreviewFading', Tekst);
-
- // SavePlayback
- Tekst := ISavePlayback[Ini.SavePlayback];
- IniFile.WriteString('Sound', 'SavePlayback', Tekst);
-
- // Lyrics Font
- Tekst := ILyricsFont[Ini.LyricsFont];
- IniFile.WriteString('Lyrics', 'LyricsFont', Tekst);
-
- // Lyrics Effect
- Tekst := ILyricsEffect[Ini.LyricsEffect];
- IniFile.WriteString('Lyrics', 'LyricsEffect', Tekst);
-
- // Solmization
- Tekst := ISolmization[Ini.Solmization];
- IniFile.WriteString('Lyrics', 'Solmization', Tekst);
-
- // Theme
- Tekst := ITheme[Ini.Theme];
- IniFile.WriteString('Themes', 'Theme', Tekst);
-
- // Skin
- Tekst := ISkin[Ini.SkinNo];
- IniFile.WriteString('Themes', 'Skin', Tekst);
-
- // Color
- Tekst := IColor[Ini.Color];
- IniFile.WriteString('Themes', 'Color', Tekst);
-
- // Record
- for I := 0 to High(CardList) do begin
- S := IntToStr(I+1);
-
- Tekst := CardList[I].Name;
- IniFile.WriteString('Record', 'DeviceName' + S, Tekst);
-
- Tekst := IntToStr(CardList[I].Input);
- IniFile.WriteString('Record', 'Input' + S, Tekst);
-
- Tekst := IntToStr(CardList[I].ChannelL);
- IniFile.WriteString('Record', 'ChannelL' + S, Tekst);
-
- Tekst := IntToStr(CardList[I].ChannelR);
- IniFile.WriteString('Record', 'ChannelR' + S, Tekst);
- end;
-
- //Log.LogError(InttoStr(Length(CardList)) + ' Cards Saved');
-
- //Advanced Settings
-
- //LoadAnimation
- Tekst := ILoadAnimation[Ini.LoadAnimation];
- IniFile.WriteString('Advanced', 'LoadAnimation', Tekst);
-
- //EffectSing
- Tekst := IEffectSing[Ini.EffectSing];
- IniFile.WriteString('Advanced', 'EffectSing', Tekst);
-
- //ScreenFade
- Tekst := IScreenFade[Ini.ScreenFade];
- IniFile.WriteString('Advanced', 'ScreenFade', Tekst);
-
- //AskbeforeDel
- Tekst := IAskbeforeDel[Ini.AskbeforeDel];
- IniFile.WriteString('Advanced', 'AskbeforeDel', Tekst);
-
- //OnSongClick
- Tekst := IOnSongClick[Ini.OnSongClick];
- IniFile.WriteString('Advanced', 'OnSongClick', Tekst);
-
- //Line Bonus
- Tekst := ILineBonus[Ini.LineBonus];
- IniFile.WriteString('Advanced', 'LineBonus', Tekst);
-
- //Party Popup
- Tekst := IPartyPopup[Ini.PartyPopup];
- IniFile.WriteString('Advanced', 'PartyPopup', Tekst);
-
- // Joypad
- Tekst := IJoypad[Ini.Joypad];
- IniFile.WriteString('Controller', 'Joypad', Tekst);
-
- IniFile.Free;
- end;
-end;
-
-procedure TIni.SaveNames;
-var
- IniFile: TIniFile;
- I: integer;
-begin
- //if not FileIsReadOnly(GamePath + 'config.ini') then begin
- //IniFile := TIniFile.Create(GamePath + 'config.ini');
- if not FileIsReadOnly(Filename) then begin
- IniFile := TIniFile.Create(Filename);
-
- //Name
- // Templates for Names Mod
- for I := 1 to 12 do
- IniFile.WriteString('Name', 'P' + IntToStr(I), Ini.Name[I-1]);
- for I := 1 to 3 do
- IniFile.WriteString('NameTeam', 'T' + IntToStr(I), Ini.NameTeam[I-1]);
- for I := 1 to 12 do
- IniFile.WriteString('NameTemplate', 'Name' + IntToStr(I), Ini.NameTemplate[I-1]);
-
- IniFile.Free;
- end;
-end;
-
-procedure TIni.SaveLevel;
-var
- IniFile: TIniFile;
- I: integer;
-begin
- //if not FileIsReadOnly(GamePath + 'config.ini') then begin
- //IniFile := TIniFile.Create(GamePath + 'config.ini');
- if not FileIsReadOnly(Filename) then begin
- IniFile := TIniFile.Create(Filename);
-
- // Difficulty
- IniFile.WriteString('Game', 'Difficulty', IDifficulty[Ini.Difficulty]);
-
- IniFile.Free;
- end;
-end;
-
-end.
+unit UIni;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses IniFiles, ULog, SysUtils;
+
+type
+ PInputDeviceConfig = ^TInputDeviceConfig;
+ TInputDeviceConfig = record
+ Name: string;
+ Input: integer;
+ ChannelToPlayerMap: array[0..1] of integer;
+ end;
+
+type
+ TIni = class
+ Name: array[0..11] of string;
+
+ // Templates for Names Mod
+ NameTeam: array[0..2] of string;
+ NameTemplate: array[0..11] of string;
+
+ //Filename of the opened iniFile
+ Filename: string;
+
+ // Game
+ Players: integer;
+ Difficulty: integer;
+ Language: integer;
+ Tabs: integer;
+ Tabs_at_startup:integer; //Tabs at Startup fix
+ Sorting: integer;
+ Debug: integer;
+
+ // Graphics
+ Screens: integer;
+ Resolution: integer;
+ Depth: integer;
+ FullScreen: integer;
+ TextureSize: integer;
+ SingWindow: integer;
+ Oscilloscope: integer;
+ Spectrum: integer;
+ Spectrograph: integer;
+ MovieSize: integer;
+
+ // Sound
+ MicBoost: integer;
+ ClickAssist: integer;
+ BeatClick: integer;
+ SavePlayback: integer;
+ Threshold: integer;
+
+ //Song Preview
+ PreviewVolume: integer;
+ PreviewFading: integer;
+
+ // Lyrics
+ LyricsFont: integer;
+ LyricsEffect: integer;
+ Solmization: integer;
+
+ // Themes
+ Theme: integer;
+ SkinNo: integer;
+ Color: integer;
+
+ // Record
+ InputDeviceConfig: array of TInputDeviceConfig;
+
+ // Advanced
+ LoadAnimation: integer;
+ EffectSing: integer;
+ ScreenFade: integer;
+ AskbeforeDel: integer;
+ OnSongClick: integer;
+ LineBonus: integer;
+ PartyPopup: integer;
+
+ // Controller
+ Joypad: integer;
+
+ // Soundcards
+ SoundCard: array[0..7, 1..2] of integer;
+
+ // Devices
+ LPT: integer;
+
+ procedure Load;
+ procedure Save;
+ procedure SaveNames;
+ procedure SaveLevel;
+ end;
+
+
+var
+ Ini: TIni;
+ IResolution: array of string;
+ ILanguage: array of string;
+ ITheme: array of string;
+ ISkin: array of string;
+ ICard: array of string;
+ IInput: array of string;
+
+const
+ IPlayers: array[0..4] of string = ('1', '2', '3', '4', '6');
+ IDifficulty: array[0..2] of string = ('Easy', 'Medium', 'Hard');
+ ITabs: array[0..1] of string = ('Off', 'On');
+
+ ISorting: array[0..7] of string = ('Edition', 'Genre', 'Language', 'Folder', 'Title', 'Artist', 'Title2', 'Artist2');
+ sEdition = 0;
+ sGenre = 1;
+ sLanguage = 2;
+ sFolder = 3;
+ sTitle = 4;
+ sArtist = 5;
+ sTitle2 = 6;
+ sArtist2 = 7;
+
+ IDebug: array[0..1] of string = ('Off', 'On');
+
+ IScreens: array[0..1] of string = ('1', '2');
+ IFullScreen: array[0..1] of string = ('Off', 'On');
+ IDepth: array[0..1] of string = ('16 bit', '32 bit');
+ ITextureSize: array[0..2] of string = ('128', '256', '512');
+ ISingWindow: array[0..1] of string = ('Small', 'Big');
+
+ //SingBar Mod
+ IOscilloscope: array[0..2] of string = ('Off', 'Osci', 'Bar');
+ //IOscilloscope: array[0..1] of string = ('Off', 'On');
+
+ ISpectrum: array[0..1] of string = ('Off', 'On');
+ ISpectrograph: array[0..1] of string = ('Off', 'On');
+ IMovieSize: array[0..2] of string = ('Half', 'Full [Vid]', 'Full [BG+Vid]');
+
+ IMicBoost: array[0..3] of string = ('Off', '+6dB', '+12dB', '+18dB');
+ IClickAssist: array[0..1] of string = ('Off', 'On');
+ IBeatClick: array[0..1] of string = ('Off', 'On');
+ ISavePlayback: array[0..1] of string = ('Off', 'On');
+ IThreshold: array[0..3] of string = ('5%', '10%', '15%', '20%');
+ //Song Preview
+ IPreviewVolume: array[0..10] of string = ('Off', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%');
+ IPreviewFading: array[0..5] of string = ('Off', '1 Sec', '2 Secs', '3 Secs', '4 Secs', '5 Secs');
+
+
+ ILyricsFont: array[0..2] of string = ('Plain', 'OLine1', 'OLine2');
+ ILyricsEffect: array[0..3] of string = ('Simple', 'Zoom', 'Slide', 'Ball');
+ ISolmization: array[0..3] of string = ('Off', 'Euro', 'Jap', 'American');
+
+ IColor: array[0..8] of string = ('Blue', 'Green', 'Pink', 'Red', 'Violet', 'Orange', 'Yellow', 'Brown', 'Black');
+
+ // Advanced
+ ILoadAnimation: array[0..1] of string = ('Off', 'On');
+ IEffectSing: array[0..1] of string = ('Off', 'On');
+ IScreenFade: array [0..1] of String =('Off', 'On');
+ IAskbeforeDel: array[0..1] of string = ('Off', 'On');
+ IOnSongClick: array[0..2] of string = ('Sing', 'Select Players', 'Open Menu');
+ ILineBonus: array[0..2] of string = ('Off', 'At Score', 'At Notes');
+ IPartyPopup: array[0..1] of string = ('Off', 'On');
+
+ IJoypad: array[0..1] of string = ('Off', 'On');
+ ILPT: array[0..2] of string = ('Off', 'LCD', 'Lights');
+
+ IChannel: array[0..6] of string = ('Off', '1', '2', '3', '4', '5', '6');
+
+implementation
+
+uses //UFiles,
+ UMain,
+ SDL,
+ ULanguage,
+ UPlatform,
+ USkins,
+ URecord,
+ UCommandLine;
+
+procedure TIni.Load;
+var
+ IniFile: TMemIniFile;
+ ThemeIni: TMemIniFile;
+ Tekst: string;
+ Pet: integer;
+ B: boolean;
+ I, I2, I3: integer;
+ S: string;
+ Modes: PPSDL_Rect;
+ SR: TSearchRec; //Skin List Patch
+
+ function GetFileName (S: String):String;
+ begin
+ //Result := copy (S,0,StrRScan (PChar(S),char('.'))+1);
+ Result := copy (S,0,Pos ('.ini',S)-1);
+ end;
+
+begin
+ GamePath := Platform.GetGameUserPath;
+
+ if (Params.ConfigFile <> '') then
+ try
+ IniFile := TMemIniFile.Create(Params.ConfigFile);
+ except
+ IniFile := TMemIniFile.Create(GamePath + 'config.ini');
+ end
+ else
+ IniFile := TMemIniFile.Create(GamePath + 'config.ini');
+
+
+ // Name
+ for I := 0 to 11 do
+ Ini.Name[I] := IniFile.ReadString('Name', 'P'+IntToStr(I+1), 'Player'+IntToStr(I+1));
+
+
+ // Templates for Names Mod
+ for I := 0 to 2 do
+ Ini.NameTeam[I] := IniFile.ReadString('NameTeam', 'T'+IntToStr(I+1), 'Team'+IntToStr(I+1));
+ for I := 0 to 11 do
+ Ini.NameTemplate[I] := IniFile.ReadString('NameTemplate', 'Name'+IntToStr(I+1), 'Template'+IntToStr(I+1));
+
+ // Players
+ Tekst := IniFile.ReadString('Game', 'Players', IPlayers[0]);
+ for Pet := 0 to High(IPlayers) do
+ if Tekst = IPlayers[Pet] then Ini.Players := Pet;
+
+ // Difficulty
+ Tekst := IniFile.ReadString('Game', 'Difficulty', 'Easy');
+ for Pet := 0 to High(IDifficulty) do
+ if Tekst = IDifficulty[Pet] then Ini.Difficulty := Pet;
+
+ // Language
+ Tekst := IniFile.ReadString('Game', 'Language', 'English');
+ for Pet := 0 to High(ILanguage) do
+ if Tekst = ILanguage[Pet] then Ini.Language := Pet;
+
+// Language.ChangeLanguage(ILanguage[Ini.Language]);
+
+ // Tabs
+ Tekst := IniFile.ReadString('Game', 'Tabs', ITabs[0]);
+ for Pet := 0 to High(ITabs) do
+ if Tekst = ITabs[Pet] then Ini.Tabs := Pet;
+
+ //Tabs at Startup fix
+ Ini.Tabs_at_startup := Ini.Tabs;
+
+ // Sorting
+ Tekst := IniFile.ReadString('Game', 'Sorting', ISorting[0]);
+ for Pet := 0 to High(ISorting) do
+ if Tekst = ISorting[Pet] then Ini.Sorting := Pet;
+
+ // Debug
+ Tekst := IniFile.ReadString('Game', 'Debug', IDebug[0]);
+ for Pet := 0 to High(IDebug) do
+ if Tekst = IDebug[Pet] then Ini.Debug := Pet;
+
+ //if Ini.Debug = 1 then SongPath := 'E:\UltraStar 03\Songs\';
+
+ // Screens
+ Tekst := IniFile.ReadString('Graphics', 'Screens', IScreens[0]);
+ for Pet := 0 to High(IScreens) do
+ if Tekst = IScreens[Pet] then Ini.Screens := Pet;
+
+ // FullScreen
+ Tekst := IniFile.ReadString('Graphics', 'FullScreen', 'On');
+ for Pet := 0 to High(IFullScreen) do
+ if Tekst = IFullScreen[Pet] then Ini.FullScreen := Pet;
+
+
+ // Resolution
+ SetLength(IResolution, 0);
+
+ Modes := SDL_ListModes(nil, SDL_OPENGL or SDL_FULLSCREEN); // Check if there are any modes available
+ while assigned( Modes^ ) do //this should solve the biggest wine problem | THANKS Linnex (11.11.07)
+ begin
+ SetLength(IResolution, Length(IResolution) + 1);
+ IResolution[High(IResolution)] := IntToStr(Modes^.w) + 'x' + IntToStr(Modes^.h);
+ Inc(Modes);
+ end;
+
+ // if no modes were set, then failback to 800x600
+ // as per http://sourceforge.net/forum/message.php?msg_id=4544965
+ // THANKS : linnex at users.sourceforge.net
+ if Length(IResolution) < 1 then
+ begin
+ SetLength(IResolution, Length(IResolution) + 1);
+ IResolution[High(IResolution)] := IntToStr(800) + 'x' + IntToStr(600);
+ Log.LogStatus('SDL_ListModes Defaulted Res To : ' + IResolution[High(IResolution)] , 'Graphics - Resolutions');
+
+ // Default to fullscreen OFF, in this case !
+ Ini.FullScreen := 0;
+ end;
+
+ // reverse order
+ for I := 0 to (Length(IResolution) div 2) - 1 do begin
+ S := IResolution[I];
+ IResolution[I] := IResolution[High(IResolution)-I];
+ IResolution[High(IResolution)-I] := S;
+ end;
+
+ Tekst := IniFile.ReadString('Graphics', 'Resolution', '800x600');
+ for Pet := 0 to High(IResolution) do
+ if Tekst = IResolution[Pet] then Ini.Resolution := Pet;
+
+
+ // Resolution
+ Tekst := IniFile.ReadString('Graphics', 'Depth', '32 bit');
+ for Pet := 0 to High(IDepth) do
+ if Tekst = IDepth[Pet] then Ini.Depth := Pet;
+
+ // Texture Size
+ Tekst := IniFile.ReadString('Graphics', 'TextureSize', ITextureSize[1]);
+ for Pet := 0 to High(ITextureSize) do
+ if Tekst = ITextureSize[Pet] then Ini.TextureSize := Pet;
+
+ // SingWindow
+ Tekst := IniFile.ReadString('Graphics', 'SingWindow', 'Big');
+ for Pet := 0 to High(ISingWindow) do
+ if Tekst = ISingWindow[Pet] then Ini.SingWindow := Pet;
+
+ // Oscilloscope
+ Tekst := IniFile.ReadString('Graphics', 'Oscilloscope', 'Bar');
+ for Pet := 0 to High(IOscilloscope) do
+ if Tekst = IOscilloscope[Pet] then Ini.Oscilloscope := Pet;
+
+ // Spectrum
+ Tekst := IniFile.ReadString('Graphics', 'Spectrum', 'Off');
+ for Pet := 0 to High(ISpectrum) do
+ if Tekst = ISpectrum[Pet] then Ini.Spectrum := Pet;
+
+ // Spectrograph
+ Tekst := IniFile.ReadString('Graphics', 'Spectrograph', 'Off');
+ for Pet := 0 to High(ISpectrograph) do
+ if Tekst = ISpectrograph[Pet] then Ini.Spectrograph := Pet;
+
+ // MovieSize
+ Tekst := IniFile.ReadString('Graphics', 'MovieSize', IMovieSize[2]);
+ for Pet := 0 to High(IMovieSize) do
+ if Tekst = IMovieSize[Pet] then Ini.MovieSize := Pet;
+
+ // MicBoost
+ Tekst := IniFile.ReadString('Sound', 'MicBoost', 'Off');
+ for Pet := 0 to High(IMicBoost) do
+ if Tekst = IMicBoost[Pet] then Ini.MicBoost := Pet;
+
+ // ClickAssist
+ Tekst := IniFile.ReadString('Sound', 'ClickAssist', 'Off');
+ for Pet := 0 to High(IClickAssist) do
+ if Tekst = IClickAssist[Pet] then Ini.ClickAssist := Pet;
+
+ // BeatClick
+ Tekst := IniFile.ReadString('Sound', 'BeatClick', IBeatClick[0]);
+ for Pet := 0 to High(IBeatClick) do
+ if Tekst = IBeatClick[Pet] then Ini.BeatClick := Pet;
+
+ // SavePlayback
+ Tekst := IniFile.ReadString('Sound', 'SavePlayback', ISavePlayback[0]);
+ for Pet := 0 to High(ISavePlayback) do
+ if Tekst = ISavePlayback[Pet] then Ini.SavePlayback := Pet;
+
+ // Threshold
+ Tekst := IniFile.ReadString('Sound', 'Threshold', IThreshold[2]);
+ for Pet := 0 to High(IThreshold) do
+ if Tekst = IThreshold[Pet] then Ini.Threshold := Pet;
+
+ //Song Preview
+ Tekst := IniFile.ReadString('Sound', 'PreviewVolume', IPreviewVolume[7]);
+ for Pet := 0 to High(IPreviewVolume) do
+ if Tekst = IPreviewVolume[Pet] then Ini.PreviewVolume := Pet;
+
+ Tekst := IniFile.ReadString('Sound', 'PreviewFading', IPreviewFading[1]);
+ for Pet := 0 to High(IPreviewFading) do
+ if Tekst = IPreviewFading[Pet] then Ini.PreviewFading := Pet;
+
+ // Lyrics Font
+ Tekst := IniFile.ReadString('Lyrics', 'LyricsFont', ILyricsFont[1]);
+ for Pet := 0 to High(ILyricsFont) do
+ if Tekst = ILyricsFont[Pet] then Ini.LyricsFont := Pet;
+
+ // Lyrics Effect
+ Tekst := IniFile.ReadString('Lyrics', 'LyricsEffect', ILyricsEffect[1]);
+ for Pet := 0 to High(ILyricsEffect) do
+ if Tekst = ILyricsEffect[Pet] then Ini.LyricsEffect := Pet;
+
+ // Solmization
+ Tekst := IniFile.ReadString('Lyrics', 'Solmization', ISolmization[0]);
+ for Pet := 0 to High(ISolmization) do
+ if Tekst = ISolmization[Pet] then Ini.Solmization := Pet;
+
+ // Theme
+
+ //Theme List Patch
+
+ //I2 Saves the no of the Deluxe (Standard-) Theme
+ I2 := 0;
+ //I counts is the cur. Theme no
+ I := 0;
+
+ SetLength(ITheme, 0);
+ writeln( 'Searching for Theme : '+ ThemePath + '*.ini' );
+ FindFirst(ThemePath + '*.ini',faAnyFile,SR);
+ Repeat
+ writeln( SR.Name );
+
+ //Read Themename from Theme
+ ThemeIni := TMemIniFile.Create(SR.Name);
+ Tekst := UpperCase(ThemeIni.ReadString('Theme','Name',GetFileName(SR.Name)));
+ ThemeIni.Free;
+
+ //if Deluxe Theme then save Themeno to I2
+ if (Tekst = 'DELUXE') then
+ I2 := I;
+
+ //Search for Skins for this Theme
+ for Pet := low(Skin.Skin) to high(Skin.Skin) do
+ begin
+ if UpperCase(Skin.Skin[Pet].Theme) = Tekst then
+ begin
+ SetLength(ITheme, Length(ITheme)+1);
+ ITheme[High(ITheme)] := GetFileName(SR.Name);
+ break;
+ end;
+ end;
+
+ Inc(I);
+ Until FindNext(SR) <> 0;
+ FindClose(SR);
+ //Theme List Patch End }
+
+ //No Theme Found
+ if (Length(ITheme)=0) then
+ begin
+ Log.CriticalError('Could not find any valid Themes.');
+ end;
+
+
+ Tekst := IniFile.ReadString('Themes', 'Theme', ITheme[I2]);
+ Ini.Theme := 0;
+ for Pet := 0 to High(ITheme) do
+ if Uppercase(Tekst) = Uppercase(ITheme[Pet]) then Ini.Theme := Pet;
+
+ // Skin
+ Skin.onThemeChange;
+ Ini.SkinNo := 0;
+
+ Tekst := IniFile.ReadString('Themes', 'Skin', ISkin[0]);
+ for Pet := 0 to High(ISkin) do
+ if Tekst = ISkin[Pet] then Ini.SkinNo := Pet;
+
+ // Color
+ Tekst := IniFile.ReadString('Themes', 'Color', IColor[0]);
+ for Pet := 0 to High(IColor) do
+ if Tekst = IColor[Pet] then Ini.Color := Pet;
+
+ // Input devices - load ini list
+ SetLength(InputDeviceConfig, 0);
+ I := 1;
+ while (IniFile.ValueExists('Record', 'DeviceName'+IntToStr(I))) do begin
+ // resize list
+ SetLength(InputDeviceConfig, Length(InputDeviceConfig)+1);
+ I2 := High(InputDeviceConfig);
+
+ // read an input device's config.
+ // Note: All devices are appended to the list whether they exist or not.
+ // Otherwise an external device's config will be lost if it is not
+ // connected (e.g. singstar mics or USB-Audio devices).
+ InputDeviceConfig[I2].Name :=
+ IniFile.ReadString('Record', 'DeviceName'+IntToStr(I), '');
+ InputDeviceConfig[I2].Input :=
+ IniFile.ReadInteger('Record', 'Input'+IntToStr(I), 0);
+ InputDeviceConfig[I2].ChannelToPlayerMap[0] :=
+ IniFile.ReadInteger('Record', 'ChannelL'+IntToStr(I), 0);
+ InputDeviceConfig[I2].ChannelToPlayerMap[1] :=
+ IniFile.ReadInteger('Record', 'ChannelR'+IntToStr(I), 0);
+
+ Inc(I);
+ end;
+
+ // Input devices - append detected soundcards
+ for I := 0 to High(AudioInputProcessor.Device) do
+ begin
+ B := False;
+ For I2 := 0 to High(InputDeviceConfig) do
+ begin //Search for Card in List
+ if (InputDeviceConfig[I2].Name = Trim(AudioInputProcessor.Device[I].Description)) then
+ begin
+ B := True;
+ // associate ini-index with device
+ AudioInputProcessor.Device[I].CfgIndex := I2;
+ Break;
+ end;
+ end;
+
+ //If not in List -> Add
+ If not B then
+ begin
+ // resize list
+ SetLength(InputDeviceConfig, Length(InputDeviceConfig)+1);
+ I2 := High(InputDeviceConfig);
+
+ InputDeviceConfig[I2].Name := Trim(AudioInputProcessor.Device[I].Description);
+ InputDeviceConfig[I2].Input := 0;
+ InputDeviceConfig[I2].ChannelToPlayerMap[0] := 0;
+ InputDeviceConfig[I2].ChannelToPlayerMap[1] := 0;
+
+ // associate ini-index with device
+ AudioInputProcessor.Device[I].CfgIndex := I2;
+
+ // set default at first start of USDX (1st device, 1st channel -> player1)
+ if (I2 = 0) then
+ InputDeviceConfig[I2].ChannelToPlayerMap[0] := 1;
+ end;
+ end;
+
+ //Advanced Settings
+
+ // LoadAnimation
+ Tekst := IniFile.ReadString('Advanced', 'LoadAnimation', 'On');
+ for Pet := 0 to High(ILoadAnimation) do
+ if Tekst = ILoadAnimation[Pet] then Ini.LoadAnimation := Pet;
+
+ // ScreenFade
+ Tekst := IniFile.ReadString('Advanced', 'ScreenFade', 'On');
+ for Pet := 0 to High(IScreenFade) do
+ if Tekst = IScreenFade[Pet] then Ini.ScreenFade := Pet;
+
+ // EffectSing
+ Tekst := IniFile.ReadString('Advanced', 'EffectSing', 'On');
+ for Pet := 0 to High(IEffectSing) do
+ if Tekst = IEffectSing[Pet] then Ini.EffectSing := Pet;
+
+ // AskbeforeDel
+ Tekst := IniFile.ReadString('Advanced', 'AskbeforeDel', 'On');
+ for Pet := 0 to High(IAskbeforeDel) do
+ if Tekst = IAskbeforeDel[Pet] then Ini.AskbeforeDel := Pet;
+
+ // OnSongClick
+ Tekst := IniFile.ReadString('Advanced', 'OnSongClick', 'Sing');
+ for Pet := 0 to High(IOnSongClick) do
+ if Tekst = IOnSongClick[Pet] then Ini.OnSongClick := Pet;
+
+ // Linebonus
+ Tekst := IniFile.ReadString('Advanced', 'LineBonus', 'At Score');
+ for Pet := 0 to High(ILineBonus) do
+ if Tekst = ILineBonus[Pet] then Ini.LineBonus := Pet;
+
+ // PartyPopup
+ Tekst := IniFile.ReadString('Advanced', 'PartyPopup', 'On');
+ for Pet := 0 to High(IPartyPopup) do
+ if Tekst = IPartyPopup[Pet] then Ini.PartyPopup := Pet;
+
+
+ // Joypad
+ Tekst := IniFile.ReadString('Controller', 'Joypad', IJoypad[0]);
+ for Pet := 0 to High(IJoypad) do
+ if Tekst = IJoypad[Pet] then Ini.Joypad := Pet;
+
+ // LCD
+ Tekst := IniFile.ReadString('Devices', 'LPT', ILPT[0]);
+ for Pet := 0 to High(ILPT) do
+ if Tekst = ILPT[Pet] then Ini.LPT := Pet;
+
+
+ // SongPath
+ if (Params.SongPath <> '') then
+ SongPath := IncludeTrailingPathDelimiter(Params.SongPath)
+ else
+ SongPath := IncludeTrailingPathDelimiter(IniFile.ReadString('Path', 'Songs', SongPath));
+
+ Filename := IniFile.FileName;
+ IniFile.Free;
+end;
+
+procedure TIni.Save;
+var
+ IniFile: TIniFile;
+ Tekst: string;
+ I: Integer;
+ S: String;
+begin
+ //if not (FileExists(GamePath + 'config.ini') and FileIsReadOnly(GamePath + 'config.ini')) then begin
+ if not (FileExists(Filename) and FileIsReadOnly(Filename)) then begin
+
+ IniFile := TIniFile.Create(Filename);
+
+ // Players
+ Tekst := IPlayers[Ini.Players];
+ IniFile.WriteString('Game', 'Players', Tekst);
+
+ // Difficulty
+ Tekst := IDifficulty[Ini.Difficulty];
+ IniFile.WriteString('Game', 'Difficulty', Tekst);
+
+ // Language
+ Tekst := ILanguage[Ini.Language];
+ IniFile.WriteString('Game', 'Language', Tekst);
+
+ // Tabs
+ Tekst := ITabs[Ini.Tabs];
+ IniFile.WriteString('Game', 'Tabs', Tekst);
+
+ // Sorting
+ Tekst := ISorting[Ini.Sorting];
+ IniFile.WriteString('Game', 'Sorting', Tekst);
+
+ // Debug
+ Tekst := IDebug[Ini.Debug];
+ IniFile.WriteString('Game', 'Debug', Tekst);
+
+ // Screens
+ Tekst := IScreens[Ini.Screens];
+ IniFile.WriteString('Graphics', 'Screens', Tekst);
+
+ // FullScreen
+ Tekst := IFullScreen[Ini.FullScreen];
+ IniFile.WriteString('Graphics', 'FullScreen', Tekst);
+
+ // Resolution
+ Tekst := IResolution[Ini.Resolution];
+ IniFile.WriteString('Graphics', 'Resolution', Tekst);
+
+ // Depth
+ Tekst := IDepth[Ini.Depth];
+ IniFile.WriteString('Graphics', 'Depth', Tekst);
+
+ // Resolution
+ Tekst := ITextureSize[Ini.TextureSize];
+ IniFile.WriteString('Graphics', 'TextureSize', Tekst);
+
+ // Sing Window
+ Tekst := ISingWindow[Ini.SingWindow];
+ IniFile.WriteString('Graphics', 'SingWindow', Tekst);
+
+ // Oscilloscope
+ Tekst := IOscilloscope[Ini.Oscilloscope];
+ IniFile.WriteString('Graphics', 'Oscilloscope', Tekst);
+
+ // Spectrum
+ Tekst := ISpectrum[Ini.Spectrum];
+ IniFile.WriteString('Graphics', 'Spectrum', Tekst);
+
+ // Spectrograph
+ Tekst := ISpectrograph[Ini.Spectrograph];
+ IniFile.WriteString('Graphics', 'Spectrograph', Tekst);
+
+ // Movie Size
+ Tekst := IMovieSize[Ini.MovieSize];
+ IniFile.WriteString('Graphics', 'MovieSize', Tekst);
+
+ // MicBoost
+ Tekst := IMicBoost[Ini.MicBoost];
+ IniFile.WriteString('Sound', 'MicBoost', Tekst);
+
+ // ClickAssist
+ Tekst := IClickAssist[Ini.ClickAssist];
+ IniFile.WriteString('Sound', 'ClickAssist', Tekst);
+
+ // BeatClick
+ Tekst := IBeatClick[Ini.BeatClick];
+ IniFile.WriteString('Sound', 'BeatClick', Tekst);
+
+ // Threshold
+ Tekst := IThreshold[Ini.Threshold];
+ IniFile.WriteString('Sound', 'Threshold', Tekst);
+
+ // Song Preview
+ Tekst := IPreviewVolume[Ini.PreviewVolume];
+ IniFile.WriteString('Sound', 'PreviewVolume', Tekst);
+
+ Tekst := IPreviewFading[Ini.PreviewFading];
+ IniFile.WriteString('Sound', 'PreviewFading', Tekst);
+
+ // SavePlayback
+ Tekst := ISavePlayback[Ini.SavePlayback];
+ IniFile.WriteString('Sound', 'SavePlayback', Tekst);
+
+ // Lyrics Font
+ Tekst := ILyricsFont[Ini.LyricsFont];
+ IniFile.WriteString('Lyrics', 'LyricsFont', Tekst);
+
+ // Lyrics Effect
+ Tekst := ILyricsEffect[Ini.LyricsEffect];
+ IniFile.WriteString('Lyrics', 'LyricsEffect', Tekst);
+
+ // Solmization
+ Tekst := ISolmization[Ini.Solmization];
+ IniFile.WriteString('Lyrics', 'Solmization', Tekst);
+
+ // Theme
+ Tekst := ITheme[Ini.Theme];
+ IniFile.WriteString('Themes', 'Theme', Tekst);
+
+ // Skin
+ Tekst := ISkin[Ini.SkinNo];
+ IniFile.WriteString('Themes', 'Skin', Tekst);
+
+ // Color
+ Tekst := IColor[Ini.Color];
+ IniFile.WriteString('Themes', 'Color', Tekst);
+
+ // Record
+ for I := 0 to High(InputDeviceConfig) do begin
+ S := IntToStr(I+1);
+
+ Tekst := InputDeviceConfig[I].Name;
+ IniFile.WriteString('Record', 'DeviceName' + S, Tekst);
+
+ Tekst := IntToStr(InputDeviceConfig[I].Input);
+ IniFile.WriteString('Record', 'Input' + S, Tekst);
+
+ Tekst := IntToStr(InputDeviceConfig[I].ChannelToPlayerMap[0]);
+ IniFile.WriteString('Record', 'ChannelL' + S, Tekst);
+
+ Tekst := IntToStr(InputDeviceConfig[I].ChannelToPlayerMap[1]);
+ IniFile.WriteString('Record', 'ChannelR' + S, Tekst);
+ end;
+
+ //Log.LogError(InttoStr(Length(CardList)) + ' Cards Saved');
+
+ //Advanced Settings
+
+ //LoadAnimation
+ Tekst := ILoadAnimation[Ini.LoadAnimation];
+ IniFile.WriteString('Advanced', 'LoadAnimation', Tekst);
+
+ //EffectSing
+ Tekst := IEffectSing[Ini.EffectSing];
+ IniFile.WriteString('Advanced', 'EffectSing', Tekst);
+
+ //ScreenFade
+ Tekst := IScreenFade[Ini.ScreenFade];
+ IniFile.WriteString('Advanced', 'ScreenFade', Tekst);
+
+ //AskbeforeDel
+ Tekst := IAskbeforeDel[Ini.AskbeforeDel];
+ IniFile.WriteString('Advanced', 'AskbeforeDel', Tekst);
+
+ //OnSongClick
+ Tekst := IOnSongClick[Ini.OnSongClick];
+ IniFile.WriteString('Advanced', 'OnSongClick', Tekst);
+
+ //Line Bonus
+ Tekst := ILineBonus[Ini.LineBonus];
+ IniFile.WriteString('Advanced', 'LineBonus', Tekst);
+
+ //Party Popup
+ Tekst := IPartyPopup[Ini.PartyPopup];
+ IniFile.WriteString('Advanced', 'PartyPopup', Tekst);
+
+ // Joypad
+ Tekst := IJoypad[Ini.Joypad];
+ IniFile.WriteString('Controller', 'Joypad', Tekst);
+
+ IniFile.Free;
+ end;
+end;
+
+procedure TIni.SaveNames;
+var
+ IniFile: TIniFile;
+ I: integer;
+begin
+ //if not FileIsReadOnly(GamePath + 'config.ini') then begin
+ //IniFile := TIniFile.Create(GamePath + 'config.ini');
+ if not FileIsReadOnly(Filename) then begin
+ IniFile := TIniFile.Create(Filename);
+
+ //Name
+ // Templates for Names Mod
+ for I := 1 to 12 do
+ IniFile.WriteString('Name', 'P' + IntToStr(I), Ini.Name[I-1]);
+ for I := 1 to 3 do
+ IniFile.WriteString('NameTeam', 'T' + IntToStr(I), Ini.NameTeam[I-1]);
+ for I := 1 to 12 do
+ IniFile.WriteString('NameTemplate', 'Name' + IntToStr(I), Ini.NameTemplate[I-1]);
+
+ IniFile.Free;
+ end;
+end;
+
+procedure TIni.SaveLevel;
+var
+ IniFile: TIniFile;
+ I: integer;
+begin
+ //if not FileIsReadOnly(GamePath + 'config.ini') then begin
+ //IniFile := TIniFile.Create(GamePath + 'config.ini');
+ if not FileIsReadOnly(Filename) then begin
+ IniFile := TIniFile.Create(Filename);
+
+ // Difficulty
+ IniFile.WriteString('Game', 'Difficulty', IDifficulty[Ini.Difficulty]);
+
+ IniFile.Free;
+ end;
+end;
+
+end.
diff --git a/Game/Code/Classes/UMain.pas b/Game/Code/Classes/UMain.pas
index 901d7370..801a66db 100644
--- a/Game/Code/Classes/UMain.pas
+++ b/Game/Code/Classes/UMain.pas
@@ -195,23 +195,14 @@ begin
Log.BenchmarkEnd(1);
Log.LogBenchmark('Loading Skin List', 1);
-(*
- // Sound Card List
- Log.BenchmarkStart(1);
- Log.LogStatus('Loading Soundcard list', 'Initialization');
- Recording := TRecord.Create;
- Log.BenchmarkEnd(1);
- Log.LogBenchmark('Loading Soundcard list', 1);
-*)
-
- // Sound (+ fills Sound Card List)
+ // Sound
Log.BenchmarkStart(1);
Log.LogStatus('Initialize Sound', 'Initialization');
InitializeSound();
Log.BenchmarkEnd(1);
Log.LogBenchmark('Initializing Sound', 1);
- // Ini + Paths
+ // Ini + Paths (depends on Sound)
Log.BenchmarkStart(1);
Log.LogStatus('Load Ini', 'Initialization');
Ini := TIni.Create;
@@ -758,7 +749,7 @@ begin
// beat click
if (Ini.BeatClick = 1) and ((Czas.AktBeatC + Czesci[0].Resolution + Czesci[0].NotesGAP) mod Czesci[0].Resolution = 0) then
- AudioPlayback.PlayClick;
+ AudioPlayback.PlaySound(SoundLib.Click);
// debug system on LPT
if ((Czas.AktBeatC + Czesci[0].Resolution + Czesci[0].NotesGAP) mod Czesci[0].Resolution = 0) then begin
@@ -780,7 +771,7 @@ begin
if (Czesci[0].Czesc[Czesci[0].Akt].Nuta[Pet].Start = Czas.AktBeatC) then begin
// click assist
if Ini.ClickAssist = 1 then
- AudioPlayback.PlayClick;
+ AudioPlayback.PlaySound(SoundLib.Click);
//LPT_2 := 0;
if ParamStr(1) <> '-doublelights' then
@@ -871,7 +862,7 @@ begin
// Czas.Ton := 27;
// gdy moze, to dodaje nute
- if (AudioInputProcessor.Sound[CP].SzczytJest) and (Mozna) then begin
+ if (AudioInputProcessor.Sound[CP].ToneValid) and (Mozna) then begin
// operowanie na ostatniej nucie
for Pet := 0 to Czesci[0].Czesc[S].HighNut do
if (Czesci[0].Czesc[S].Nuta[Pet].Start <= Czas.OldBeatD+1)
@@ -880,11 +871,11 @@ begin
// to robi, tylko dla pary nut (oryginalnej i gracza)
// przesuwanie tonu w odpowiednia game
- while (AudioInputProcessor.Sound[CP].Ton - Czesci[0].Czesc[S].Nuta[Pet].Ton > 6) do
- AudioInputProcessor.Sound[CP].Ton := AudioInputProcessor.Sound[CP].Ton - 12;
+ while (AudioInputProcessor.Sound[CP].Tone - Czesci[0].Czesc[S].Nuta[Pet].Ton > 6) do
+ AudioInputProcessor.Sound[CP].Tone := AudioInputProcessor.Sound[CP].Tone - 12;
- while (AudioInputProcessor.Sound[CP].Ton - Czesci[0].Czesc[S].Nuta[Pet].Ton < -6) do
- AudioInputProcessor.Sound[CP].Ton := AudioInputProcessor.Sound[CP].Ton + 12;
+ while (AudioInputProcessor.Sound[CP].Tone - Czesci[0].Czesc[S].Nuta[Pet].Ton < -6) do
+ AudioInputProcessor.Sound[CP].Tone := AudioInputProcessor.Sound[CP].Tone + 12;
// Half size Notes Patch
NoteHit := false;
@@ -894,8 +885,8 @@ begin
//if Ini.Difficulty = 2 then Range := 0;
Range := 2 - Ini.Difficulty;
- if abs(Czesci[0].Czesc[S].Nuta[Pet].Ton - AudioInputProcessor.Sound[CP].Ton) <= Range then begin
- AudioInputProcessor.Sound[CP].Ton := Czesci[0].Czesc[S].Nuta[Pet].Ton;
+ if abs(Czesci[0].Czesc[S].Nuta[Pet].Ton - AudioInputProcessor.Sound[CP].Tone) <= Range then begin
+ AudioInputProcessor.Sound[CP].Tone := Czesci[0].Czesc[S].Nuta[Pet].Ton;
// Half size Notes Patch
@@ -936,7 +927,7 @@ begin
Nowa := true;
// jezeli ostatnia ma ten sam ton
if (Player[CP].IlNut > 0 )
- and (Player[CP].Nuta[Player[CP].HighNut].Ton = AudioInputProcessor.Sound[CP].Ton)
+ and (Player[CP].Nuta[Player[CP].HighNut].Ton = AudioInputProcessor.Sound[CP].Tone)
and (Player[CP].Nuta[Player[CP].HighNut].Start + Player[CP].Nuta[Player[CP].HighNut].Dlugosc = Czas.AktBeatD)
then Nowa := false;
// jezeli jest jakas nowa nuta na sprawdzanym beacie
@@ -952,7 +943,7 @@ begin
SetLength(Player[CP].Nuta, Player[CP].IlNut);
Player[CP].Nuta[Player[CP].HighNut].Start := Czas.AktBeatD;
Player[CP].Nuta[Player[CP].HighNut].Dlugosc := 1;
- Player[CP].Nuta[Player[CP].HighNut].Ton := AudioInputProcessor.Sound[CP].Ton; // Ton || TonDokl
+ Player[CP].Nuta[Player[CP].HighNut].Ton := AudioInputProcessor.Sound[CP].Tone; // Ton || TonDokl
Player[CP].Nuta[Player[CP].HighNut].Detekt := Czas.MidBeat;
diff --git a/Game/Code/Classes/UMedia_dummy.pas b/Game/Code/Classes/UMedia_dummy.pas
index c973512d..cd62dc51 100644
--- a/Game/Code/Classes/UMedia_dummy.pas
+++ b/Game/Code/Classes/UMedia_dummy.pas
@@ -1,268 +1,206 @@
-unit UMedia_dummy;
-{< #############################################################################
-# FFmpeg support for UltraStar deluxe #
-# #
-# Created by b1indy #
-# based on 'An ffmpeg and SDL Tutorial' (http://www.dranger.com/ffmpeg/) #
-# #
-# http://www.mail-archive.com/fpc-pascal@lists.freepascal.org/msg09949.html #
-# http://www.nabble.com/file/p11795857/mpegpas01.zip #
-# #
-############################################################################## }
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-implementation
-
-uses
- SysUtils,
- math,
- UMusic;
-
-
-var
- singleton_dummy : IVideoPlayback;
-
-type
- Tmedia_dummy = class( TInterfacedObject, IVideoPlayback, IVideoVisualization, IAudioPlayback, IAudioInput )
- private
- public
- constructor create();
- function GetName: String;
-
- procedure init();
-
- function Open( aFileName : string): boolean; // true if succeed
- procedure Close;
-
- procedure Play;
- procedure Pause;
- procedure Stop;
-
- procedure SetPosition(Time: real);
- function GetPosition: real;
-
- procedure GetFrame(Time: Extended);
- procedure DrawGL(Screen: integer);
-
- // IAudioInput
- procedure InitializeRecord;
- procedure CaptureStart;
- procedure CaptureStop;
- procedure CaptureCard(RecordI, PlayerLeft, PlayerRight: byte);
- procedure StopCard(Card: byte);
- function GetFFTData: TFFTData;
- function GetPCMData(var data: TPCMData): Cardinal;
-
- // IAudioPlayback
- procedure InitializePlayback;
- procedure SetVolume(Volume: integer);
- procedure SetMusicVolume(Volume: integer);
- procedure SetLoop(Enabled: boolean);
- procedure Rewind;
-
- function Finished: boolean;
- function Length: real;
-
- procedure PlayStart;
- procedure PlayBack;
- procedure PlaySwoosh;
- procedure PlayChange;
- procedure PlayOption;
- procedure PlayClick;
- procedure PlayDrum;
- procedure PlayHihat;
- procedure PlayClap;
- procedure PlayShuffle;
- procedure StopShuffle;
-
- function LoadCustomSound(const Filename: String): Cardinal;
- procedure PlayCustomSound(const Index: Cardinal );
-
- end;
-
-
-
-function Tmedia_dummy.GetName: String;
-begin
- result := 'dummy';
-end;
-
-
-procedure Tmedia_dummy.GetFrame(Time: Extended);
-begin
-end;
-
-procedure Tmedia_dummy.DrawGL(Screen: integer);
-begin
-end;
-
-constructor Tmedia_dummy.create();
-begin
-end;
-
-procedure Tmedia_dummy.init();
-begin
-end;
-
-
-function Tmedia_dummy.Open( aFileName : string): boolean; // true if succeed
-begin
- result := false;
-end;
-
-procedure Tmedia_dummy.Close;
-begin
-end;
-
-procedure Tmedia_dummy.Play;
-begin
-end;
-
-procedure Tmedia_dummy.Pause;
-begin
-end;
-
-procedure Tmedia_dummy.Stop;
-begin
-end;
-
-procedure Tmedia_dummy.SetPosition(Time: real);
-begin
-end;
-
-function Tmedia_dummy.getPosition: real;
-begin
- result := 0;
-end;
-
-// IAudioInput
-procedure Tmedia_dummy.InitializeRecord;
-begin
-end;
-
-procedure Tmedia_dummy.CaptureStart;
-begin
-end;
-
-procedure Tmedia_dummy.CaptureStop;
-begin
-end;
-
-procedure Tmedia_dummy.CaptureCard(RecordI, PlayerLeft, PlayerRight: byte);
-begin
-end;
-
-procedure Tmedia_dummy.StopCard(Card: byte);
-begin
-end;
-
-function Tmedia_dummy.GetFFTData: TFFTData;
-var data: TFFTData;
-begin
- result := data;
-end;
-
-function Tmedia_dummy.GetPCMData(var data: TPCMData): Cardinal;
-begin
- result := 0;
-end;
-
-// IAudioPlayback
-procedure Tmedia_dummy.InitializePlayback;
-begin
-end;
-
-procedure Tmedia_dummy.SetVolume(Volume: integer);
-begin
-end;
-
-procedure Tmedia_dummy.SetMusicVolume(Volume: integer);
-begin
-end;
-
-procedure Tmedia_dummy.SetLoop(Enabled: boolean);
-begin
-end;
-
-procedure Tmedia_dummy.Rewind;
-begin
-end;
-
-function Tmedia_dummy.Finished: boolean;
-begin
- result := false;
-end;
-
-function Tmedia_dummy.Length: real;
-begin
- Result := 60;
-end;
-
-procedure Tmedia_dummy.PlayStart;
-begin
-end;
-
-procedure Tmedia_dummy.PlayBack;
-begin
-end;
-
-procedure Tmedia_dummy.PlaySwoosh;
-begin
-end;
-
-procedure Tmedia_dummy.PlayChange;
-begin
-end;
-
-procedure Tmedia_dummy.PlayOption;
-begin
-end;
-
-procedure Tmedia_dummy.PlayClick;
-begin
-end;
-
-procedure Tmedia_dummy.PlayDrum;
-begin
-end;
-
-procedure Tmedia_dummy.PlayHihat;
-begin
-end;
-
-procedure Tmedia_dummy.PlayClap;
-begin
-end;
-
-procedure Tmedia_dummy.PlayShuffle;
-begin
-end;
-
-procedure Tmedia_dummy.StopShuffle;
-begin
-end;
-
-function Tmedia_dummy.LoadCustomSound(const Filename: String): Cardinal;
-begin
- result := 0;
-end;
-
-procedure Tmedia_dummy.PlayCustomSound(const Index: Cardinal );
-begin
-end;
-
-initialization
- singleton_dummy := Tmedia_dummy.create();
- AudioManager.add( singleton_dummy );
-
-finalization
- AudioManager.Remove( singleton_dummy );
-
-end.
+unit UMedia_dummy;
+{< #############################################################################
+# FFmpeg support for UltraStar deluxe #
+# #
+# Created by b1indy #
+# based on 'An ffmpeg and SDL Tutorial' (http://www.dranger.com/ffmpeg/) #
+# #
+# http://www.mail-archive.com/fpc-pascal@lists.freepascal.org/msg09949.html #
+# http://www.nabble.com/file/p11795857/mpegpas01.zip #
+# #
+############################################################################## }
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+implementation
+
+uses
+ SysUtils,
+ math,
+ UMusic;
+
+
+var
+ singleton_dummy : IVideoPlayback;
+
+type
+ Tmedia_dummy = class( TInterfacedObject, IVideoPlayback, IVideoVisualization, IAudioPlayback, IAudioInput )
+ private
+ public
+ constructor create();
+ function GetName: String;
+
+ procedure init();
+
+ function Open( aFileName : string): boolean; // true if succeed
+ procedure Close;
+
+ procedure Play;
+ procedure Pause;
+ procedure Stop;
+
+ procedure SetPosition(Time: real);
+ function GetPosition: real;
+
+ procedure GetFrame(Time: Extended);
+ procedure DrawGL(Screen: integer);
+
+ // IAudioInput
+ function InitializeRecord: boolean;
+ procedure CaptureStart;
+ procedure CaptureStop;
+ procedure GetFFTData(var data: TFFTData);
+ function GetPCMData(var data: TPCMData): Cardinal;
+
+ // IAudioPlayback
+ function InitializePlayback: boolean;
+ procedure SetVolume(Volume: integer);
+ procedure SetMusicVolume(Volume: integer);
+ procedure SetLoop(Enabled: boolean);
+ procedure Rewind;
+
+ function Finished: boolean;
+ function Length: real;
+
+ function OpenSound(const Filename: String): TAudioPlaybackStream;
+ procedure PlaySound(stream: TAudioPlaybackStream);
+ procedure StopSound(stream: TAudioPlaybackStream);
+ end;
+
+
+
+function Tmedia_dummy.GetName: String;
+begin
+ result := 'dummy';
+end;
+
+
+procedure Tmedia_dummy.GetFrame(Time: Extended);
+begin
+end;
+
+procedure Tmedia_dummy.DrawGL(Screen: integer);
+begin
+end;
+
+constructor Tmedia_dummy.create();
+begin
+end;
+
+procedure Tmedia_dummy.init();
+begin
+end;
+
+
+function Tmedia_dummy.Open( aFileName : string): boolean; // true if succeed
+begin
+ result := false;
+end;
+
+procedure Tmedia_dummy.Close;
+begin
+end;
+
+procedure Tmedia_dummy.Play;
+begin
+end;
+
+procedure Tmedia_dummy.Pause;
+begin
+end;
+
+procedure Tmedia_dummy.Stop;
+begin
+end;
+
+procedure Tmedia_dummy.SetPosition(Time: real);
+begin
+end;
+
+function Tmedia_dummy.getPosition: real;
+begin
+ result := 0;
+end;
+
+// IAudioInput
+function Tmedia_dummy.InitializeRecord: boolean;
+begin
+ result := true;
+end;
+
+procedure Tmedia_dummy.CaptureStart;
+begin
+end;
+
+procedure Tmedia_dummy.CaptureStop;
+begin
+end;
+
+procedure Tmedia_dummy.GetFFTData(var data: TFFTData);
+begin
+end;
+
+function Tmedia_dummy.GetPCMData(var data: TPCMData): Cardinal;
+begin
+ result := 0;
+end;
+
+// IAudioPlayback
+function Tmedia_dummy.InitializePlayback: boolean;
+begin
+ result := true;
+end;
+
+procedure Tmedia_dummy.SetVolume(Volume: integer);
+begin
+end;
+
+procedure Tmedia_dummy.SetMusicVolume(Volume: integer);
+begin
+end;
+
+procedure Tmedia_dummy.SetLoop(Enabled: boolean);
+begin
+end;
+
+procedure Tmedia_dummy.Rewind;
+begin
+end;
+
+function Tmedia_dummy.Finished: boolean;
+begin
+ result := false;
+end;
+
+function Tmedia_dummy.Length: real;
+begin
+ Result := 60;
+end;
+
+function Tmedia_dummy.OpenSound(const Filename: String): TAudioPlaybackStream;
+begin
+ result := nil;
+end;
+
+procedure Tmedia_dummy.PlaySound(stream: TAudioPlaybackStream);
+begin
+end;
+
+procedure Tmedia_dummy.StopSound(stream: TAudioPlaybackStream);
+begin
+end;
+
+initialization
+ singleton_dummy := Tmedia_dummy.create();
+ AudioManager.add( singleton_dummy );
+
+finalization
+ AudioManager.Remove( singleton_dummy );
+
+end.
diff --git a/Game/Code/Classes/UMusic.pas b/Game/Code/Classes/UMusic.pas
index a7f1918f..27ab86e1 100644
--- a/Game/Code/Classes/UMusic.pas
+++ b/Game/Code/Classes/UMusic.pas
@@ -1,423 +1,492 @@
-unit UMusic;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses Classes ;
-
-type
- TMuzyka = record
- Path: string;
- Start: integer; // start of song in ms
- IlNut: integer;
- DlugoscNut: integer;
- end;
-
- PLine = ^TLine;
- TLine = record
- Start: integer;
- StartNote: integer;
- Lyric: string;
- LyricWidth: real;
- Koniec: integer;
- BaseNote: integer;
- HighNut: integer;
- IlNut: integer;
- TotalNotes: integer;
- Nuta: array of record
- Color: integer;
- Start: integer;
- Dlugosc: integer;
- Ton: integer;
- TonGamy: integer;
- Tekst: string;
- FreeStyle: boolean;
- Wartosc: integer; // zwykla nuta x1, zlota nuta x2
- end;
- end;
- ALine = array of TLine;
-
- TCzesci = record
- Akt: integer; // aktualna czesc utworu do rysowania
- High: integer;
- Ilosc: integer;
- Resolution: integer;
- NotesGAP: integer;
- Wartosc: integer;
- Czesc: ALine;
- end;
-
- TCzas = record // wszystko, co dotyczy aktualnej klatki
- OldBeat: integer; // poprzednio wykryty beat w utworze
- AktBeat: integer; // aktualny beat w utworze
- MidBeat: real; // dokladny AktBeat
-
- // now we use this for super synchronization!
- // only used when analyzing voice
- OldBeatD: integer; // poprzednio wykryty beat w utworze
- AktBeatD: integer; // aktualny beat w utworze
- MidBeatD: real; // dokladny AktBeatD
- FracBeatD: real; // fractional part of MidBeatD
-
- // we use this for audiable clicks
- OldBeatC: integer; // poprzednio wykryty beat w utworze
- AktBeatC: integer; // aktualny beat w utworze
- MidBeatC: real; // dokladny AktBeatC
- FracBeatC: real; // fractional part of MidBeatC
-
-
- OldCzesc: integer; // poprzednio wyswietlana czesc
- // akt jest w czesci.akt
-
- Teraz: real; // aktualny czas w utworze
- Razem: real; // caly czas utworu
- end;
-
- TSoundCard = record
- Name: string;
- Source: array of string;
- end;
-
-type
- TFFTData = array[0..256] of Single;
-
- TPCMStereoSample = array[0..1] of Smallint;
- TPCMData = array[0..511] of TPCMStereoSample;
-
-type
- TStreamStatus = (sStopped, sPlaying, sPaused);
-const
- StreamStatusStr: array[TStreamStatus] of string = ('Stopped', 'Playing', 'Paused');
-
-type
- TAudioProcessingStream = class
- public
- procedure Close(); virtual; abstract;
- end;
-
- TAudioPlaybackStream = class(TAudioProcessingStream)
- protected
- function GetLoop(): boolean; virtual; abstract;
- procedure SetLoop(Enabled: boolean); virtual; abstract;
- function GetLength(): real; virtual; abstract;
- function GetStatus(): TStreamStatus; virtual; abstract;
- public
- procedure Play(); virtual; abstract;
- procedure Pause(); virtual; abstract;
- procedure Stop(); virtual; abstract;
-
- property Loop: boolean READ GetLoop WRITE SetLoop;
- property Length: real READ GetLength;
- property Status: TStreamStatus READ GetStatus;
- end;
-
- (*
- TAudioMixerStream = class(TAudioProcessingStream)
- procedure AddStream(stream: TAudioProcessingStream);
- procedure RemoveStream(stream: TAudioProcessingStream);
- procedure SetMasterVolume(volume: cardinal);
- function GetMasterVolume(): cardinal;
- procedure SetStreamVolume(stream: TAudioProcessingStream; volume: cardinal);
- function GetStreamVolume(stream: TAudioProcessingStream): cardinal;
- end;
- *)
-
- TAudioDecodeStream = class(TAudioProcessingStream)
- protected
- function GetLength(): real; virtual; abstract;
- function GetChannelCount(): cardinal; virtual; abstract;
- function GetSampleRate(): cardinal; virtual; abstract;
- function GetPosition(): real; virtual; abstract;
- procedure SetPosition(Time: real); virtual; abstract;
- function IsEOF(): boolean; virtual; abstract;
- public
- function ReadData(Buffer: PChar; BufSize: integer): integer; virtual; abstract;
-
- property Length: real READ GetLength;
- property ChannelCount: cardinal READ GetChannelCount;
- property SampleRate: cardinal READ GetSampleRate;
- property Position: real READ GetPosition WRITE SetPosition;
- property EOF: boolean READ IsEOF;
- end;
-
-type
- TCustomSoundEntry = record
- Filename : String;
- Stream : TAudioPlaybackStream;
- end;
-
-type
- IGenericPlayback = Interface
- ['{63A5EBC3-3F4D-4F23-8DFB-B5165FCE33DD}']
- function GetName: String;
-
- function Open(Filename: string): boolean; // true if succeed
- procedure Close;
-
- procedure Play;
- procedure Pause;
- procedure Stop;
-
- procedure SetPosition(Time: real);
- function GetPosition: real;
-
- property Position : real READ GetPosition WRITE SetPosition;
- end;
-
- IVideoPlayback = Interface( IGenericPlayback )
- ['{3574C40C-28AE-4201-B3D1-3D1F0759B131}']
- procedure init();
-
- procedure GetFrame(Time: Extended); // WANT TO RENAME THESE TO BE MORE GENERIC
- procedure DrawGL(Screen: integer); // WANT TO RENAME THESE TO BE MORE GENERIC
-
- end;
-
- IVideoVisualization = Interface( IVideoPlayback )
- ['{5AC17D60-B34D-478D-B632-EB00D4078017}']
- end;
-
- IAudioPlayback = Interface( IGenericPlayback )
- ['{E4AE0B40-3C21-4DC5-847C-20A87E0DFB96}']
- procedure InitializePlayback;
- procedure SetVolume(Volume: integer);
- procedure SetMusicVolume(Volume: integer);
- procedure SetLoop(Enabled: boolean);
-
- procedure Rewind;
- function Finished: boolean;
- function Length: real;
-
- procedure PlayStart;
- procedure PlayBack;
- procedure PlaySwoosh;
- procedure PlayChange;
- procedure PlayOption;
- procedure PlayClick;
- procedure PlayDrum;
- procedure PlayHihat;
- procedure PlayClap;
- procedure PlayShuffle;
- procedure StopShuffle;
-
- //Custom Sounds
- function LoadCustomSound(const Filename: String): Cardinal;
- procedure PlayCustomSound(const Index: Cardinal );
-
- //Equalizer
- function GetFFTData: TFFTData;
-
- // Interface for Visualizer
- function GetPCMData(var data: TPCMData): Cardinal;
- end;
-
- IGenericDecoder = Interface
- ['{557B0E9A-604D-47E4-B826-13769F3E10B7}']
- function InitializeDecoder(): boolean;
- //function IsSupported(const Filename: string): boolean;
- end;
-
- (*
- IVideoDecoder = Interface( IGenericDecoder )
- ['{2F184B2B-FE69-44D5-9031-0A2462391DCA}']
- function Open(const Filename: string): TVideoDecodeStream;
- end;
- *)
-
- IAudioDecoder = Interface( IGenericDecoder )
- ['{AB47B1B6-2AA9-4410-BF8C-EC79561B5478}']
- function Open(const Filename: string): TAudioDecodeStream;
- end;
-
- IAudioInput = Interface
- ['{A5C8DA92-2A0C-4AB2-849B-2F7448C6003A}']
- function GetName: String;
- procedure InitializeRecord;
-
- procedure CaptureStart;
- procedure CaptureStop;
- end;
-
-
-var // TODO : JB --- THESE SHOULD NOT BE GLOBAL
- // muzyka
- Muzyka: TMuzyka;
-
- // czesci z nutami;
- Czesci: array of TCzesci;
-
- // czas
- Czas: TCzas;
-
-
-procedure InitializeSound;
-
-function Visualization(): IVideoPlayback;
-function VideoPlayback(): IVideoPlayback;
-function AudioPlayback(): IAudioPlayback;
-function AudioInput(): IAudioInput;
-function AudioDecoder(): IAudioDecoder;
-
-function AudioManager: TInterfaceList;
-
-
-implementation
-
-uses
- sysutils,
- UCommandLine;
-// uLog;
-
-var
- singleton_VideoPlayback : IVideoPlayback = nil;
- singleton_Visualization : IVideoPlayback = nil;
- singleton_AudioPlayback : IAudioPlayback = nil;
- singleton_AudioInput : IAudioInput = nil;
- singleton_AudioDecoder : IAudioDecoder = nil;
-
- singleton_AudioManager : TInterfaceList = nil;
-
-
-function AudioManager: TInterfaceList;
-begin
- if singleton_AudioManager = nil then
- singleton_AudioManager := TInterfaceList.Create();
-
- Result := singleton_AudioManager;
-end; //CompressionPluginManager
-
-
-function VideoPlayback(): IVideoPlayback;
-begin
- result := singleton_VideoPlayback;
-end;
-
-function Visualization(): IVideoPlayback;
-begin
- result := singleton_Visualization;
-end;
-
-function AudioPlayback(): IAudioPlayback;
-begin
- result := singleton_AudioPlayback;
-end;
-
-function AudioInput(): IAudioInput;
-begin
- result := singleton_AudioInput;
-end;
-
-function AudioDecoder(): IAudioDecoder;
-begin
- result := singleton_AudioDecoder;
-end;
-
-procedure InitializeSound;
-var
- lTmpInterface : IInterface;
- iCount : Integer;
-begin
- lTmpInterface := nil;
-
- singleton_AudioPlayback := nil;
- singleton_AudioInput := nil;
- singleton_AudioDecoder := nil;
- singleton_VideoPlayback := nil;
- singleton_Visualization := nil;
-
- for iCount := 0 to AudioManager.Count - 1 do
- begin
- if assigned( AudioManager[iCount] ) then
- begin
- // if this interface is a Playback, then set it as the default used
-
- if ( AudioManager[iCount].QueryInterface( IAudioPlayback, lTmpInterface ) = 0 ) AND
- ( true ) then
- begin
- singleton_AudioPlayback := IAudioPlayback( lTmpInterface );
- end;
-
- // if this interface is a Input, then set it as the default used
- if ( AudioManager[iCount].QueryInterface( IAudioInput, lTmpInterface ) = 0 ) AND
- ( true ) then
- begin
- singleton_AudioInput := IAudioInput( lTmpInterface );
- end;
-
- // if this interface is a Decoder, then set it as the default used
- if ( AudioManager[iCount].QueryInterface( IAudioDecoder, lTmpInterface ) = 0 ) AND
- ( true ) then
- begin
- singleton_AudioDecoder := IAudioDecoder( lTmpInterface );
- end;
-
- // if this interface is a Input, then set it as the default used
- if ( AudioManager[iCount].QueryInterface( IVideoPlayback, lTmpInterface ) = 0 ) AND
- ( true ) then
- begin
- singleton_VideoPlayback := IVideoPlayback( lTmpInterface );
- end;
-
- if ( AudioManager[iCount].QueryInterface( IVideoVisualization, lTmpInterface ) = 0 ) AND
- ( true ) then
- begin
- singleton_Visualization := IVideoPlayback( lTmpInterface );
- end;
-
- end;
- end;
-
-
-
- if VideoPlayback <> nil then
- begin
- end;
-
- if AudioDecoder <> nil then
- begin
- AudioDecoder.InitializeDecoder;
- end;
-
- if AudioPlayback <> nil then
- begin
- AudioPlayback.InitializePlayback;
- end;
-
- if AudioInput <> nil then
- begin
- AudioInput.InitializeRecord;
- end;
-
- if FindCmdLineSwitch( cMediaInterfaces ) then
- begin
- writeln( '' );
- writeln( '--------------------------------------------------------------' );
- writeln( ' In-use Media Interfaces ' );
- writeln( '--------------------------------------------------------------' );
- writeln( 'Registered Audio Playback Interface : ' + AudioPlayback.GetName );
- writeln( 'Registered Audio Input Interface : ' + AudioInput.GetName );
- writeln( 'Registered Video Playback Interface : ' + VideoPlayback.GetName );
- writeln( 'Registered Visualization Interface : ' + Visualization.GetName );
- writeln( '--------------------------------------------------------------' );
- writeln( '' );
-
- halt;
- end;
-end;
-
-initialization
-begin
- singleton_AudioManager := TInterfaceList.Create();
-
-end;
-
-finalization
- singleton_AudioManager.clear;
- FreeAndNil( singleton_AudioManager );
-
-end.
+unit UMusic;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ Classes;
+
+type
+ TNoteType = (ntFreestyle, ntNormal, ntGolden);
+
+ //http://paste.ubuntu-nl.org/51892/
+
+ TMuzyka = record // (TODO: rename to TMusic/TMelody?)
+ Path: string;
+ Start: integer; // start of song in ms
+ IlNut: integer; // (TODO: Il = tone, Nut(a) = Note)
+ DlugoscNut: integer; // (TODO: Dlugosc = length, Nut(a) = Note)
+ end;
+
+ PLine = ^TLine;
+ TLine = record // (TODO: rename to TSentence?)
+ Start: integer;
+ StartNote: integer;
+ Lyric: string;
+ LyricWidth: real;
+ Koniec: integer; // (TODO: rename to End_/Ending?)
+ BaseNote: integer;
+ HighNut: integer; // (TODO: rename to HighNote)
+ IlNut: integer; // (TODO: Il = tone, Nut(a) = Note)
+ TotalNotes: integer;
+ Nuta: array of record // (TODO: rename to Note)
+ Color: integer;
+ Start: integer;
+ Dlugosc: integer; // (TODO: rename to Length)
+ Ton: integer; // full range tone (TODO: rename to Tone)
+ TonGamy: integer; // tone unified to one octave (TODO: rename to something meaningful, ToneGamus)
+ Tekst: string; // (TODO: rename to Text)
+ FreeStyle: boolean;
+ Wartosc: integer; // normal-note: 1, golden-note: 2 (TODO: wartosc=value, rename to Type_ or Kind?)
+ end;
+ end;
+ ALine = array of TLine; // (TODO: rename to TLineArray)
+
+ // (TCzesci = TSentences)
+ TCzesci = record
+ Akt: integer; // for drawing of current line (Akt = Current)
+ High: integer;
+ Ilosc: integer; // (TODO: Ilosc = Number/Count)
+ Resolution: integer;
+ NotesGAP: integer;
+ Wartosc: integer; // TODO: rename (wartosc=value)
+ Czesc: ALine; // TODO: rename to Sentence or Line
+ end;
+
+ // (TODO: rename TCzas to something like T(Line/Sentence)Time/TLinePosition/TLineState)
+ // (Czas = time)
+ TCzas = record // all that concerns the current frames
+ OldBeat: integer; // previous discovered beat
+ AktBeat: integer; // current beat (TODO: rename)
+ MidBeat: real; // like AktBeat
+
+ // now we use this for super synchronization!
+ // only used when analyzing voice
+ OldBeatD: integer; // previous discovered beat
+ AktBeatD: integer; // current beat (TODO: rename)
+ MidBeatD: real; // like AktBeatD
+ FracBeatD: real; // fractional part of MidBeatD
+
+ // we use this for audible clicks
+ OldBeatC: integer; // previous discovered beat
+ AktBeatC: integer; // current beat (TODO: rename)
+ MidBeatC: real; // like AktBeatC
+ FracBeatC: real; // fractional part of MidBeatC
+
+
+ OldCzesc: integer; // previous displayed sentence (Czesc = part (here: sentence/line))
+
+ Teraz: real; // (TODO: Teraz = current time)
+ Razem: real; // (TODO: Razem = total time)
+ end;
+
+
+type
+ TFFTData = array[0..255] of Single;
+
+ TPCMStereoSample = array[0..1] of Smallint;
+ TPCMData = array[0..511] of TPCMStereoSample;
+
+type
+ TStreamStatus = (ssStopped, ssPlaying, ssPaused, ssBlocked, ssUnknown);
+const
+ StreamStatusStr: array[TStreamStatus] of string =
+ ('Stopped', 'Playing', 'Paused', 'Blocked', 'Unknown');
+
+type
+ TAudioSampleFormat = (
+ asfU8, asfS8, // unsigned/signed 8 bits
+ asfU16LSB, asfS16LSB, // unsigned/signed 16 bits (endianness: LSB)
+ asfU16MSB, asfS16MSB, // unsigned/signed 16 bits (endianness: MSB)
+ asfU16, asfS16, // unsigned/signed 16 bits (endianness: System)
+ asfS24, // signed 24 bits (endianness: System)
+ asfS32, // signed 32 bits (endianness: System)
+ asfFloat // float
+ );
+
+ TAudioFormatInfo = record
+ Channels: byte;
+ SampleRate: integer;
+ Format: TAudioSampleFormat;
+ end;
+
+type
+ TAudioProcessingStream = class
+ public
+ procedure Close(); virtual; abstract;
+ end;
+
+ TAudioPlaybackStream = class(TAudioProcessingStream)
+ protected
+ function GetLoop(): boolean; virtual; abstract;
+ procedure SetLoop(Enabled: boolean); virtual; abstract;
+ function GetLength(): real; virtual; abstract;
+ function GetStatus(): TStreamStatus; virtual; abstract;
+ function GetVolume(): integer; virtual; abstract;
+ procedure SetVolume(volume: integer); virtual; abstract;
+ public
+ procedure Play(); virtual; abstract;
+ procedure Pause(); virtual; abstract;
+ procedure Stop(); virtual; abstract;
+
+ property Loop: boolean READ GetLoop WRITE SetLoop;
+ property Length: real READ GetLength;
+ property Status: TStreamStatus READ GetStatus;
+ property Volume: integer READ GetVolume WRITE SetVolume;
+ end;
+
+ (*
+ TAudioMixerStream = class(TAudioProcessingStream)
+ procedure AddStream(stream: TAudioProcessingStream);
+ procedure RemoveStream(stream: TAudioProcessingStream);
+ procedure SetMasterVolume(volume: cardinal);
+ function GetMasterVolume(): cardinal;
+ procedure SetStreamVolume(stream: TAudioProcessingStream; volume: cardinal);
+ function GetStreamVolume(stream: TAudioProcessingStream): cardinal;
+ end;
+ *)
+
+ TAudioDecodeStream = class(TAudioProcessingStream)
+ protected
+ function GetLength(): real; virtual; abstract;
+ function GetPosition(): real; virtual; abstract;
+ procedure SetPosition(Time: real); virtual; abstract;
+ function IsEOF(): boolean; virtual; abstract;
+ public
+ function ReadData(Buffer: PChar; BufSize: integer): integer; virtual; abstract;
+ function GetAudioFormatInfo(): TAudioFormatInfo; virtual; abstract;
+
+ property Length: real READ GetLength;
+ property Position: real READ GetPosition WRITE SetPosition;
+ property EOF: boolean READ IsEOF;
+ end;
+
+type
+ IGenericPlayback = Interface
+ ['{63A5EBC3-3F4D-4F23-8DFB-B5165FCE33DD}']
+ function GetName: String;
+
+ function Open(Filename: string): boolean; // true if succeed
+ procedure Close;
+
+ procedure Play;
+ procedure Pause;
+ procedure Stop;
+
+ procedure SetPosition(Time: real);
+ function GetPosition: real;
+
+ property Position : real READ GetPosition WRITE SetPosition;
+ end;
+
+ IVideoPlayback = Interface( IGenericPlayback )
+ ['{3574C40C-28AE-4201-B3D1-3D1F0759B131}']
+ procedure init();
+
+ procedure GetFrame(Time: Extended); // WANT TO RENAME THESE TO BE MORE GENERIC
+ procedure DrawGL(Screen: integer); // WANT TO RENAME THESE TO BE MORE GENERIC
+
+ end;
+
+ IVideoVisualization = Interface( IVideoPlayback )
+ ['{5AC17D60-B34D-478D-B632-EB00D4078017}']
+ end;
+
+ IAudioPlayback = Interface( IGenericPlayback )
+ ['{E4AE0B40-3C21-4DC5-847C-20A87E0DFB96}']
+ function InitializePlayback: boolean;
+ procedure SetVolume(Volume: integer);
+ procedure SetMusicVolume(Volume: integer);
+ procedure SetLoop(Enabled: boolean);
+
+ procedure Rewind;
+ function Finished: boolean;
+ function Length: real;
+
+ // Sounds
+ function OpenSound(const Filename: String): TAudioPlaybackStream;
+ procedure PlaySound(stream: TAudioPlaybackStream);
+ procedure StopSound(stream: TAudioPlaybackStream);
+
+ // Equalizer
+ procedure GetFFTData(var data: TFFTData);
+
+ // Interface for Visualizer
+ function GetPCMData(var data: TPCMData): Cardinal;
+ end;
+
+ IGenericDecoder = Interface
+ ['{557B0E9A-604D-47E4-B826-13769F3E10B7}']
+ function InitializeDecoder(): boolean;
+ //function IsSupported(const Filename: string): boolean;
+ end;
+
+ (*
+ IVideoDecoder = Interface( IGenericDecoder )
+ ['{2F184B2B-FE69-44D5-9031-0A2462391DCA}']
+ function Open(const Filename: string): TVideoDecodeStream;
+ end;
+ *)
+
+ IAudioDecoder = Interface( IGenericDecoder )
+ ['{AB47B1B6-2AA9-4410-BF8C-EC79561B5478}']
+ function Open(const Filename: string): TAudioDecodeStream;
+ end;
+
+ IAudioInput = Interface
+ ['{A5C8DA92-2A0C-4AB2-849B-2F7448C6003A}']
+ function GetName: String;
+ function InitializeRecord: boolean;
+
+ procedure CaptureStart;
+ procedure CaptureStop;
+ end;
+
+type
+ TSoundLibrary = class
+ public
+ Start: TAudioPlaybackStream;
+ Back: TAudioPlaybackStream;
+ Swoosh: TAudioPlaybackStream;
+ Change: TAudioPlaybackStream;
+ Option: TAudioPlaybackStream;
+ Click: TAudioPlaybackStream;
+ Drum: TAudioPlaybackStream;
+ Hihat: TAudioPlaybackStream;
+ Clap: TAudioPlaybackStream;
+ Shuffle: TAudioPlaybackStream;
+
+ constructor Create();
+ destructor Destroy(); override;
+ end;
+
+var // TODO : JB --- THESE SHOULD NOT BE GLOBAL
+ // music
+ Muzyka: TMuzyka; // TODO: rename
+
+ // czesci z nutami;
+ Czesci: array of TCzesci; // TODO: rename to Sentences/Lines
+
+ // czas
+ Czas: TCzas; // TODO: rename
+
+ SoundLib: TSoundLibrary;
+
+
+procedure InitializeSound;
+
+function Visualization(): IVideoPlayback;
+function VideoPlayback(): IVideoPlayback;
+function AudioPlayback(): IAudioPlayback;
+function AudioInput(): IAudioInput;
+function AudioDecoder(): IAudioDecoder;
+
+function AudioManager: TInterfaceList;
+
+
+implementation
+
+uses
+ sysutils,
+ UMain,
+ UCommandLine;
+// uLog;
+
+var
+ singleton_VideoPlayback : IVideoPlayback = nil;
+ singleton_Visualization : IVideoPlayback = nil;
+ singleton_AudioPlayback : IAudioPlayback = nil;
+ singleton_AudioInput : IAudioInput = nil;
+ singleton_AudioDecoder : IAudioDecoder = nil;
+
+ singleton_AudioManager : TInterfaceList = nil;
+
+
+function AudioManager: TInterfaceList;
+begin
+ if singleton_AudioManager = nil then
+ singleton_AudioManager := TInterfaceList.Create();
+
+ Result := singleton_AudioManager;
+end; //CompressionPluginManager
+
+
+function VideoPlayback(): IVideoPlayback;
+begin
+ result := singleton_VideoPlayback;
+end;
+
+function Visualization(): IVideoPlayback;
+begin
+ result := singleton_Visualization;
+end;
+
+function AudioPlayback(): IAudioPlayback;
+begin
+ result := singleton_AudioPlayback;
+end;
+
+function AudioInput(): IAudioInput;
+begin
+ result := singleton_AudioInput;
+end;
+
+function AudioDecoder(): IAudioDecoder;
+begin
+ result := singleton_AudioDecoder;
+end;
+
+procedure InitializeSound;
+var
+ lTmpInterface : IInterface;
+ iCount : Integer;
+begin
+ lTmpInterface := nil;
+
+ singleton_AudioPlayback := nil;
+ singleton_AudioInput := nil;
+ singleton_AudioDecoder := nil;
+ singleton_VideoPlayback := nil;
+ singleton_Visualization := nil;
+
+ for iCount := 0 to AudioManager.Count - 1 do
+ begin
+ if assigned( AudioManager[iCount] ) then
+ begin
+ // if this interface is a Playback, then set it as the default used
+
+ if ( AudioManager[iCount].QueryInterface( IAudioPlayback, lTmpInterface ) = 0 ) AND
+ ( true ) then
+ begin
+ singleton_AudioPlayback := IAudioPlayback( lTmpInterface );
+ end;
+
+ // if this interface is a Input, then set it as the default used
+ if ( AudioManager[iCount].QueryInterface( IAudioInput, lTmpInterface ) = 0 ) AND
+ ( true ) then
+ begin
+ singleton_AudioInput := IAudioInput( lTmpInterface );
+ end;
+
+ // if this interface is a Decoder, then set it as the default used
+ if ( AudioManager[iCount].QueryInterface( IAudioDecoder, lTmpInterface ) = 0 ) AND
+ ( true ) then
+ begin
+ singleton_AudioDecoder := IAudioDecoder( lTmpInterface );
+ end;
+
+ // if this interface is a Input, then set it as the default used
+ if ( AudioManager[iCount].QueryInterface( IVideoPlayback, lTmpInterface ) = 0 ) AND
+ ( true ) then
+ begin
+ singleton_VideoPlayback := IVideoPlayback( lTmpInterface );
+ end;
+
+ if ( AudioManager[iCount].QueryInterface( IVideoVisualization, lTmpInterface ) = 0 ) AND
+ ( true ) then
+ begin
+ singleton_Visualization := IVideoPlayback( lTmpInterface );
+ end;
+
+ end;
+ end;
+
+
+
+ if VideoPlayback <> nil then
+ begin
+ end;
+
+ if AudioDecoder <> nil then
+ begin
+ AudioDecoder.InitializeDecoder;
+ end;
+
+ if AudioPlayback <> nil then
+ begin
+ AudioPlayback.InitializePlayback;
+ end;
+
+ if AudioInput <> nil then
+ begin
+ AudioInput.InitializeRecord;
+ end;
+
+ // Load in-game sounds
+ SoundLib := TSoundLibrary.Create;
+
+ if FindCmdLineSwitch( cMediaInterfaces ) then
+ begin
+ writeln( '' );
+ writeln( '--------------------------------------------------------------' );
+ writeln( ' In-use Media Interfaces ' );
+ writeln( '--------------------------------------------------------------' );
+ writeln( 'Registered Audio Playback Interface : ' + AudioPlayback.GetName );
+ writeln( 'Registered Audio Input Interface : ' + AudioInput.GetName );
+ writeln( 'Registered Video Playback Interface : ' + VideoPlayback.GetName );
+ writeln( 'Registered Visualization Interface : ' + Visualization.GetName );
+ writeln( '--------------------------------------------------------------' );
+ writeln( '' );
+
+ halt;
+ end;
+end;
+
+constructor TSoundLibrary.Create();
+begin
+ //Log.LogStatus('Loading Sounds', 'Music Initialize');
+
+ //Log.BenchmarkStart(4);
+
+ Start := AudioPlayback.OpenSound(SoundPath + 'Common start.mp3');
+ (*
+ Back := AudioPlayback.OpenSound(SoundPath + 'Common back.mp3');
+ Swoosh := AudioPlayback.OpenSound(SoundPath + 'menu swoosh.mp3');
+ Change := AudioPlayback.OpenSound(SoundPath + 'select music change music 50.mp3');
+ Option := AudioPlayback.OpenSound(SoundPath + 'option change col.mp3');
+ Click := AudioPlayback.OpenSound(SoundPath + 'rimshot022b.mp3');
+ *)
+
+ //Drum := AudioPlayback.OpenSound(SoundPath + 'bassdrumhard076b.mp3');
+ //Hihat := AudioPlayback.OpenSound(SoundPath + 'hihatclosed068b.mp3');
+ //Clap := AudioPlayback.OpenSound(SoundPath + 'claps050b.mp3');
+
+ //Shuffle := AudioPlayback.OpenSound(SoundPath + 'Shuffle.mp3');
+
+ //Log.BenchmarkEnd(4);
+ //Log.LogBenchmark('--> Loading Sounds', 4);
+end;
+
+destructor TSoundLibrary.Destroy();
+begin
+ Start.Free;
+ Back.Free;
+ Swoosh.Free;
+ Change.Free;
+ Option.Free;
+ Click.Free;
+
+ //Drum.Free;
+ //Hihat.Free;
+ //Clap.Free;
+
+ //Shuffle.Free;
+end;
+
+
+initialization
+begin
+ singleton_AudioManager := TInterfaceList.Create();
+
+end;
+
+finalization
+ singleton_AudioManager.clear;
+ FreeAndNil( singleton_AudioManager );
+
+end.
diff --git a/Game/Code/Classes/URecord.pas b/Game/Code/Classes/URecord.pas
index ab351f6e..8ae0978a 100644
--- a/Game/Code/Classes/URecord.pas
+++ b/Game/Code/Classes/URecord.pas
@@ -1,325 +1,535 @@
-unit URecord;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses Classes,
- Math,
- SysUtils,
- UCommon,
- UMusic,
- UIni;
-
-// http://www.poltran.com
-
-type
- TSound = class
- BufferNew: TMemoryStream; // buffer for newest sample
- BufferArray: array[1..4096] of smallint; // (Signal) newest 4096 samples
- BufferLong: array of TMemoryStream; // full buffer
-
- Num: integer;
- n: integer; // length of Signal to analyze
-
- // pitch detection
- SzczytJest: boolean; // czy jest szczyt
- pivot : integer; // Position of summit (top) on horizontal pivot
- TonDokl: real; // ton aktualnego szczytu
- Ton: integer; // ton bez ulamka
- TonGamy: integer; // ton w gamie. wartosci: 0-11
- Skala: real; // skala FFT
-
- // procedures
- procedure ProcessNewBuffer;
- procedure AnalyzeBuffer; // use to analyze sound from buffers to get new pitch
- procedure AnalyzeByAutocorrelation; // we call it to analyze sound by checking Autocorrelation
- function AnalyzeAutocorrelationFreq(Freq: real): real; // use this to check one frequency by Autocorrelation
- end;
-
- TSoundCardInput = record
- Name: string;
- end;
-
- TGenericSoundCard = class
- // here can be the soundcard information - whole database from which user will select recording source
- Description: string; // soundcard name/description
- Input: array of TSoundCardInput; // soundcard input(-source)s
- InputSelected: integer; // unused. What is this good for?
- MicInput: integer; // unused. What is this good for?
- //SampleRate: integer; // TODO: for sample-rate conversion (for devices that do not support 44.1kHz)
- CaptureSoundLeft: TSound; // sound(-buffer) used for left channel capture data
- CaptureSoundRight: TSound; // sound(-buffer) used for right channel capture data
- end;
-
- TAudioInputProcessor = class
- Sound: array of TSound;
- SoundCard: array of TGenericSoundCard;
-
- constructor Create;
-
- // handle microphone input
- procedure HandleMicrophoneData(Buffer: Pointer; Length: Cardinal;
- InputDevice: TGenericSoundCard);
-
- function volume( aChannel : byte ): byte;
- end;
-
- smallintarray = array [0..maxInt shr 1-1] of smallInt;
- psmallintarray = ^smallintarray;
-
- function AudioInputProcessor(): TAudioInputProcessor;
-
-implementation
-
-uses UMain;
-
-var
- singleton_AudioInputProcessor : TAudioInputProcessor = nil;
-
-
-// FIXME: Race-Conditions between Callback-thread and main-thread
-// on BufferArray (maybe BufferNew also).
-// Use SDL-mutexes to solve this problem.
-
-
-function AudioInputProcessor(): TAudioInputProcessor;
-begin
+unit URecord;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses Classes,
+ Math,
+ SysUtils,
+ UCommon,
+ UMusic,
+ UIni;
+
+type
+ TSound = class
+ private
+ BufferNew: TMemoryStream; // buffer for newest samples
+ public
+ BufferArray: array[0..4095] of smallint; // newest 4096 samples
+ BufferLong: array of TMemoryStream; // full buffer
+
+ Index: integer; // index in TAudioInputProcessor.Sound[] (TODO: Remove if not used)
+
+ AnalysisBufferSize: integer; // number of samples to analyze
+
+ // pitch detection
+ ToneValid: boolean; // true if Tone contains a valid value (otherwise it contains noise)
+ //Peak: integer; // position of peak on horizontal pivot (TODO: Remove if not used)
+ //ToneAccuracy: real; // tone accuracy (TODO: Remove if not used)
+ Tone: integer; // TODO: should be a non-unified full range tone (e.g. C2<>C3). Range: 0..NumHalftones-1
+ // Note: at the moment it is the same as ToneUnified
+ ToneUnified: integer; // tone unified to one octave (e.g. C2=C3=C4). Range: 0-11
+ //Scale: real; // FFT scale (TODO: Remove if not used)
+
+ // procedures
+ procedure ProcessNewBuffer;
+ procedure AnalyzeBuffer; // use to analyze sound from buffers to get new pitch
+ procedure AnalyzeByAutocorrelation; // we call it to analyze sound by checking Autocorrelation
+ function AnalyzeAutocorrelationFreq(Freq: real): real; // use this to check one frequency by Autocorrelation
+ end;
+
+ TAudioInputDeviceSource = record
+ Name: string;
+ end;
+
+ // soundcard input-devices information
+ TAudioInputDevice = class
+ public
+ CfgIndex: integer; // index of this device in Ini.InputDeviceConfig
+ Description: string; // soundcard name/description
+ Source: array of TAudioInputDeviceSource; // soundcard input(-source)s
+ SourceSelected: integer; // unused. What is this good for?
+ MicInput: integer; // unused. What is this good for?
+ SampleRate: integer; // capture sample-rate (e.g. 44.1kHz -> 44100)
+ CaptureChannel: array[0..1] of TSound; // sound(-buffers) used for left/right channel's capture data
+
+ procedure Start(); virtual; abstract;
+ procedure Stop(); virtual; abstract;
+
+ destructor Destroy; override;
+ end;
+
+ TAudioInputProcessor = class
+ Sound: array of TSound;
+ Device: array of TAudioInputDevice;
+
+ constructor Create;
+
+ // handle microphone input
+ procedure HandleMicrophoneData(Buffer: Pointer; Size: Cardinal;
+ InputDevice: TAudioInputDevice);
+
+ function Volume( aChannel : byte ): byte;
+ end;
+
+ TAudioInputBase = class( TInterfacedObject, IAudioInput )
+ private
+ Started: boolean;
+ protected
+ function UnifyDeviceName(const name: string; deviceIndex: integer): string;
+ function UnifyDeviceSourceName(const name: string; const deviceName: string): string;
+ public
+ function GetName: String; virtual; abstract;
+ function InitializeRecord: boolean; virtual; abstract;
+
+ procedure CaptureStart;
+ procedure CaptureStop;
+ end;
+
+
+ SmallIntArray = array [0..maxInt shr 1-1] of smallInt;
+ PSmallIntArray = ^SmallIntArray;
+
+ function AudioInputProcessor(): TAudioInputProcessor;
+
+implementation
+
+uses
+ ULog,
+ UMain;
+
+const
+ CaptureFreq = 44100;
+ BaseToneFreq = 65.4064; // lowest (half-)tone to analyze (C2 = 65.4064 Hz)
+ NumHalftones = 36; // C2-B4 (for Whitney and my high voice)
+
+var
+ singleton_AudioInputProcessor : TAudioInputProcessor = nil;
+
+
+// FIXME: Race-Conditions between Callback-thread and main-thread
+// on BufferArray (maybe BufferNew also).
+// Use SDL-mutexes to solve this problem.
+
+
+{ Global }
+
+function AudioInputProcessor(): TAudioInputProcessor;
+begin
if singleton_AudioInputProcessor = nil then
singleton_AudioInputProcessor := TAudioInputProcessor.create();
result := singleton_AudioInputProcessor;
-
end;
-
-procedure TSound.ProcessNewBuffer;
-var
- S: integer;
- L: integer;
- A: integer;
-begin
- // process BufferArray
- S := 0;
- L := BufferNew.Size div 2;
- if L > n then begin
- S := L - n;
- L := n;
- end;
-
- // copy to array
- for A := L+1 to n do
- BufferArray[A-L] := BufferArray[A];
-
- BufferNew.Seek(2*S, soBeginning);
- BufferNew.ReadBuffer(BufferArray[1+n-L], 2*L);
-
- // process BufferLong
- if Ini.SavePlayback = 1 then
- begin
- BufferNew.Seek(0, soBeginning);
- BufferLong[0].CopyFrom(BufferNew, BufferNew.Size);
- end;
-end;
-
-procedure TSound.AnalyzeBuffer;
-begin
- AnalyzeByAutocorrelation;
-end;
-
-procedure TSound.AnalyzeByAutocorrelation;
-var
- T: integer; // tone
- F: real; // freq
- Wages: array[0..35] of real; // wages
- MaxT: integer; // max tone
- MaxW: real; // max wage
- V: real; // volume
- MaxV: real; // max volume
- S: integer; // Signal
- Threshold: real; // threshold
-begin
- SzczytJest := false;
-
- // find maximum volume of first 1024 words of signal
- MaxV := 0;
- for S := 1 to 1024 do // 0.5.2: fix. was from 0 to 1023
- begin
- V := Abs(BufferArray[S]) / $10000;
-
- if V > MaxV then
- MaxV := V;
- end;
-
- // prepare to analyze
- MaxW := 0;
-
- // analyze all 12 halftones
- for T := 0 to 35 do // to 11, then 23, now 35 (for Whitney and my high voice)
- begin
- F := 130.81 * Power(1.05946309436, T)/2; // let's analyze below 130.81
- Wages[T] := AnalyzeAutocorrelationFreq(F);
-
- if Wages[T] > MaxW then
- begin // this frequency has better wage
- MaxW := Wages[T];
- MaxT := T;
- end;
- end; // for T
-
- Threshold := 0.1;
- case Ini.Threshold of
- 0: Threshold := 0.05;
- 1: Threshold := 0.1;
- 2: Threshold := 0.15;
- 3: Threshold := 0.2;
- end;
-
- if MaxV >= Threshold then
- begin // found acceptable volume // 0.1
- SzczytJest := true;
- TonGamy := MaxT mod 12;
- Ton := MaxT mod 12;
- end;
-
-end;
-
-function TSound.AnalyzeAutocorrelationFreq(Freq: real): real; // result medium difference
-var
- Count: real;
- Src: integer;
- Dst: integer;
- Move: integer;
- Il: integer; // how many counts were done
-begin
- // we use Signal as source
- Count := 0;
- Il := 0;
- Src := 1;
- Move := Round(44100/Freq);
- Dst := Src + Move;
-
- // ver 2 - compare in vertical
- while (Dst < n) do
- begin // process up to n (4KB) of Signal
- Count := Count + Abs(BufferArray[Src] - BufferArray[Dst]) / $10000;
- Inc(Src);
- Inc(Dst);
- Inc(Il);
- end;
-
- Result := 1 - Count / Il;
-end;
-
-{*
- * Handle captured microphone input data.
- * Params:
- * Buffer - buffer of signed 16bit interleaved stereo PCM-samples.
- * Interleaved means that a right-channel sample follows a left-
- * channel sample and vice versa (0:left[0],1:right[0],2:left[1],...).
- * Length - number of bytes in Buffer
- * Input - Soundcard-Input used for capture
- *}
-procedure TAudioInputProcessor.HandleMicrophoneData(Buffer: Pointer; Length: Cardinal; InputDevice: TGenericSoundCard);
-var
- L: integer;
- S: integer;
- PB: pbytearray;
- PSI: psmallintarray;
- I: integer;
- Skip: integer;
- Boost: byte;
-begin
- // set boost
- case Ini.MicBoost of
- 0: Boost := 1;
- 1: Boost := 2;
- 2: Boost := 4;
- 3: Boost := 8;
- end;
-
- // boost buffer
- L := Length div 2; // number of samples
- PSI := Buffer;
- for S := 0 to L-1 do
- begin
- I := PSI^[S] * Boost;
-
- // TODO : JB - This will clip the audio... cant we reduce the "Boot" if the data clips ??
- if I > 32767 then
- I := 32767; // 0.5.0: limit
-
- if I < -32768 then
- I := -32768; // 0.5.0: limit
-
- PSI^[S] := I;
- end;
-
- // 2 players USB mic, left channel
- if InputDevice.CaptureSoundLeft <> nil then
- begin
- L := Length div 4; // number of samples
- PB := Buffer;
-
- InputDevice.CaptureSoundLeft.BufferNew.Clear; // 0.5.2: problem on exiting
- for S := 0 to L-1 do
- begin
- InputDevice.CaptureSoundLeft.BufferNew.Write(PB[S*4], 2);
- end;
- InputDevice.CaptureSoundLeft.ProcessNewBuffer;
- end;
-
- // 2 players USB mic, right channel
- Skip := 2;
-
- if InputDevice.CaptureSoundRight <> nil then
- begin
- L := Length div 4; // number of samples
- PB := Buffer;
- InputDevice.CaptureSoundRight.BufferNew.Clear;
- for S := 0 to L-1 do
- begin
- InputDevice.CaptureSoundRight.BufferNew.Write(PB[Skip + S*4], 2);
- end;
- InputDevice.CaptureSoundRight.ProcessNewBuffer;
- end;
-end;
-
-constructor TAudioInputProcessor.Create;
-var
- S: integer;
-begin
- SetLength(Sound, 6 {max players});//Ini.Players+1);
- for S := 0 to High(Sound) do
- begin //Ini.Players do begin
- Sound[S] := TSound.Create;
- Sound[S].Num := S;
- Sound[S].BufferNew := TMemoryStream.Create;
- SetLength(Sound[S].BufferLong, 1);
- Sound[S].BufferLong[0] := TMemoryStream.Create;
- Sound[S].n := 4*1024;
- end;
-end;
-
-function TAudioInputProcessor.volume( aChannel : byte ): byte;
-var
- lCount : Integer;
- lMaxVol : double;
-begin;
- lMaxVol := AudioInputProcessor.Sound[aChannel].BufferArray[1];
- for lCount := 2 to AudioInputProcessor.Sound[aChannel].n div 1 do
- begin
- if AudioInputProcessor.Sound[aChannel].BufferArray[lCount] > lMaxVol then
- lMaxVol := AudioInputProcessor.Sound[aChannel].BufferArray[lCount];
- end;
-
- result := trunc( ( 255 / 32767 ) * trunc( lMaxVol ) );
-end;
-
-end.
-
-
-
+
+
+{ TAudioInputDevice }
+
+destructor TAudioInputDevice.Destroy;
+var
+ i: integer;
+begin
+ Stop();
+ Source := nil;
+ for i := 0 to High(CaptureChannel) do
+ CaptureChannel[i] := nil;
+ inherited Destroy;
+end;
+
+
+{ TSound }
+
+procedure TSound.ProcessNewBuffer;
+var
+ SkipCount: integer;
+ NumSamples: integer;
+ SampleIndex: integer;
+begin
+ // process BufferArray
+ SkipCount := 0;
+ NumSamples := BufferNew.Size div 2;
+
+ // check if we have more new samples than we can store
+ if NumSamples > Length(BufferArray) then
+ begin
+ // discard the oldest of the new samples
+ SkipCount := NumSamples - Length(BufferArray);
+ NumSamples := Length(BufferArray);
+ end;
+
+ // move old samples to the beginning of the array (if necessary)
+ for SampleIndex := NumSamples to High(BufferArray) do
+ BufferArray[SampleIndex-NumSamples] := BufferArray[SampleIndex];
+
+ // skip samples if necessary
+ BufferNew.Seek(2*SkipCount, soBeginning);
+ // copy samples
+ BufferNew.ReadBuffer(BufferArray[Length(BufferArray)-NumSamples], 2*NumSamples);
+
+ // save capture-data to BufferLong if neccessary
+ if Ini.SavePlayback = 1 then
+ begin
+ BufferNew.Seek(0, soBeginning);
+ BufferLong[0].CopyFrom(BufferNew, BufferNew.Size);
+ end;
+end;
+
+procedure TSound.AnalyzeBuffer;
+begin
+ AnalyzeByAutocorrelation;
+end;
+
+procedure TSound.AnalyzeByAutocorrelation;
+var
+ ToneIndex: integer;
+ Freq: real;
+ Wages: array[0..NumHalftones-1] of real;
+ MaxTone: integer;
+ MaxWage: real;
+ Volume: real;
+ MaxVolume: real;
+ SampleIndex: integer;
+ Threshold: real;
+const
+ HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
+begin
+ ToneValid := false;
+
+ // find maximum volume of first 1024 samples
+ MaxVolume := 0;
+ for SampleIndex := 0 to 1023 do
+ begin
+ Volume := Abs(BufferArray[SampleIndex]) /
+ -Low(Smallint); // was $10000 (65536) before but must be 32768
+
+ if Volume > MaxVolume then
+ MaxVolume := Volume;
+ end;
+
+ // prepare to analyze
+ MaxWage := 0;
+
+ // analyze halftones
+ for ToneIndex := 0 to NumHalftones-1 do
+ begin
+ Freq := BaseToneFreq * Power(HalftoneBase, ToneIndex);
+ Wages[ToneIndex] := AnalyzeAutocorrelationFreq(Freq);
+
+ if Wages[ToneIndex] > MaxWage then
+ begin
+ // this frequency has better wage
+ MaxWage := Wages[ToneIndex];
+ MaxTone := ToneIndex;
+ end;
+ end;
+
+ Threshold := 0.2;
+ case Ini.Threshold of
+ 0: Threshold := 0.1;
+ 1: Threshold := 0.2;
+ 2: Threshold := 0.3;
+ 3: Threshold := 0.4;
+ end;
+
+ // check if signal has an acceptable volume (ignore background-noise)
+ if MaxVolume >= Threshold then
+ begin
+ ToneValid := true;
+ ToneUnified := MaxTone mod 12;
+ Tone := MaxTone mod 12;
+ end;
+
+end;
+
+function TSound.AnalyzeAutocorrelationFreq(Freq: real): real; // result medium difference
+var
+ Dist: real; // distance (0=equal .. 1=totally different) between correlated samples
+ AccumDist: real; // accumulated distances
+ SampleIndex: integer; // index of sample to analyze
+ CorrelatingSampleIndex: integer; // index of sample one period ahead
+ SamplesPerPeriod: integer; // samples in one period
+begin
+ SampleIndex := 0;
+ SamplesPerPeriod := Round(CaptureFreq/Freq);
+ CorrelatingSampleIndex := SampleIndex + SamplesPerPeriod;
+
+ AccumDist := 0;
+
+ // compare correlating samples
+ while (CorrelatingSampleIndex < AnalysisBufferSize) do
+ begin
+ // calc distance (correlation: 1-dist) to corresponding sample in next period
+ Dist := Abs(BufferArray[SampleIndex] - BufferArray[CorrelatingSampleIndex]) /
+ High(Word); // was $10000 (65536) before but must be 65535
+ AccumDist := AccumDist + Dist;
+ Inc(SampleIndex);
+ Inc(CorrelatingSampleIndex);
+ end;
+
+ // return "inverse" average distance (=correlation)
+ Result := 1 - AccumDist / AnalysisBufferSize;
+end;
+
+
+{ TAudioInputProcessor }
+
+{*
+ * Handle captured microphone input data.
+ * Params:
+ * Buffer - buffer of signed 16bit interleaved stereo PCM-samples.
+ * Interleaved means that a right-channel sample follows a left-
+ * channel sample and vice versa (0:left[0],1:right[0],2:left[1],...).
+ * Length - number of bytes in Buffer
+ * Input - Soundcard-Input used for capture
+ *}
+procedure TAudioInputProcessor.HandleMicrophoneData(Buffer: Pointer; Size: Cardinal; InputDevice: TAudioInputDevice);
+var
+ NumSamples: integer; // number of samples
+ SampleIndex: integer;
+ Value: integer;
+ ByteBuffer: PByteArray; // buffer handled as array of bytes
+ SampleBuffer: PSmallIntArray; // buffer handled as array of samples
+ Offset: integer;
+ Boost: byte;
+ ChannelCount: integer;
+ ChannelIndex: integer;
+ CaptureChannel: TSound;
+ SampleSize: integer;
+begin
+ // set boost
+ case Ini.MicBoost of
+ 0: Boost := 1;
+ 1: Boost := 2;
+ 2: Boost := 4;
+ 3: Boost := 8;
+ end;
+
+ // boost buffer
+ NumSamples := Size div 2;
+ SampleBuffer := Buffer;
+ for SampleIndex := 0 to NumSamples-1 do
+ begin
+ Value := SampleBuffer^[SampleIndex] * Boost;
+
+ // TODO : JB - This will clip the audio... cant we reduce the "Boost" if the data clips ??
+ if Value > High(Smallint) then
+ Value := High(Smallint);
+
+ if Value < Low(Smallint) then
+ Value := Low(Smallint);
+
+ SampleBuffer^[SampleIndex] := Value;
+ end;
+
+ // number of channels
+ ChannelCount := Length(InputDevice.CaptureChannel);
+ // size of one sample
+ SampleSize := ChannelCount * SizeOf(SmallInt);
+ // samples per channel
+ NumSamples := Size div SampleSize;
+
+ // interpret buffer as buffer of bytes
+ ByteBuffer := Buffer;
+
+ // process channels
+ for ChannelIndex := 0 to High(InputDevice.CaptureChannel) do
+ begin
+ CaptureChannel := InputDevice.CaptureChannel[ChannelIndex];
+ if (CaptureChannel <> nil) then
+ begin
+ Offset := ChannelIndex * SizeOf(SmallInt);
+
+ // TODO: remove BufferNew and write to BufferArray directly
+
+ CaptureChannel.BufferNew.Clear;
+ for SampleIndex := 0 to NumSamples-1 do
+ begin
+ CaptureChannel.BufferNew.Write(ByteBuffer^[Offset + SampleIndex*SampleSize],
+ SizeOf(SmallInt));
+ end;
+ CaptureChannel.ProcessNewBuffer();
+ end;
+ end;
+end;
+
+constructor TAudioInputProcessor.Create;
+var
+ i: integer;
+begin
+ SetLength(Sound, 6 {max players});//Ini.Players+1);
+ for i := 0 to High(Sound) do
+ begin
+ Sound[i] := TSound.Create;
+ Sound[i].Index := i;
+ Sound[i].BufferNew := TMemoryStream.Create;
+ SetLength(Sound[i].BufferLong, 1);
+ Sound[i].BufferLong[0] := TMemoryStream.Create;
+ Sound[i].AnalysisBufferSize := Min(4*1024, Length(Sound[i].BufferArray));
+ end;
+end;
+
+function TAudioInputProcessor.Volume( aChannel : byte ): byte;
+var
+ lSampleIndex: Integer;
+ lMaxVol : Word;
+begin;
+ with AudioInputProcessor.Sound[aChannel] do
+ begin
+ lMaxVol := BufferArray[0];
+ for lSampleIndex := 1 to High(BufferArray) do
+ begin
+ if Abs(BufferArray[lSampleIndex]) > lMaxVol then
+ lMaxVol := Abs(BufferArray[lSampleIndex]);
+ end;
+ end;
+
+ result := trunc( ( 255 / -Low(Smallint) ) * lMaxVol );
+end;
+
+
+{ TAudioInputBase }
+
+{*
+ * Start capturing on all used input-device.
+ *}
+procedure TAudioInputBase.CaptureStart;
+var
+ S: integer;
+ DeviceIndex: integer;
+ ChannelIndex: integer;
+ Device: TAudioInputDevice;
+ DeviceCfg: PInputDeviceConfig;
+ DeviceUsed: boolean;
+ Player: integer;
+begin
+ if (Started) then
+ CaptureStop();
+
+ Log.BenchmarkStart(1);
+
+ // reset buffers
+ for S := 0 to High(AudioInputProcessor.Sound) do
+ AudioInputProcessor.Sound[S].BufferLong[0].Clear;
+
+ // start capturing on each used device
+ for DeviceIndex := 0 to High(AudioInputProcessor.Device) do begin
+ Device := AudioInputProcessor.Device[DeviceIndex];
+ if not assigned(Device) then
+ continue;
+ DeviceCfg := @Ini.InputDeviceConfig[Device.CfgIndex];
+
+ DeviceUsed := false;
+
+ // check if device is used
+ for ChannelIndex := 0 to High(DeviceCfg.ChannelToPlayerMap) do
+ begin
+ Player := DeviceCfg.ChannelToPlayerMap[ChannelIndex]-1;
+ if (Player < 0) or (Player >= PlayersPlay) then
+ begin
+ Device.CaptureChannel[ChannelIndex] := nil;
+ end
+ else
+ begin
+ Device.CaptureChannel[ChannelIndex] := AudioInputProcessor.Sound[Player];
+ DeviceUsed := true;
+ end;
+ end;
+
+ // start device if used
+ if (DeviceUsed) then begin
+ Log.BenchmarkStart(2);
+ Device.Start();
+ Log.BenchmarkEnd(2);
+ Log.LogBenchmark('Device.Start', 2) ;
+ end;
+ end;
+
+ Log.BenchmarkEnd(1);
+ Log.LogBenchmark('CaptureStart', 1) ;
+
+ Started := true;
+end;
+
+{*
+ * Stop input-capturing on all soundcards.
+ *}
+procedure TAudioInputBase.CaptureStop;
+var
+ DeviceIndex: integer;
+ Player: integer;
+ Device: TAudioInputDevice;
+ DeviceCfg: PInputDeviceConfig;
+begin
+ for DeviceIndex := 0 to High(AudioInputProcessor.Device) do begin
+ Device := AudioInputProcessor.Device[DeviceIndex];
+ if not assigned(Device) then
+ continue;
+ Device.Stop();
+ end;
+
+ Started := false;
+end;
+
+function TAudioInputBase.UnifyDeviceName(const name: string; deviceIndex: integer): string;
+var
+ count: integer; // count of devices with this name
+
+ function IsDuplicate(const name: string): boolean;
+ var
+ i: integer;
+ begin
+ Result := False;
+ // search devices with same description
+ For i := 0 to deviceIndex-1 do
+ begin
+ if (AudioInputProcessor.Device[i].Description = name) then
+ begin
+ Result := True;
+ Break;
+ end;
+ end;
+ end;
+begin
+ count := 1;
+ result := name;
+
+ // if there is another device with the same ID, search for an available name
+ while (IsDuplicate(result)) do
+ begin
+ Inc(count);
+ // set description
+ result := name + ' ('+IntToStr(count)+')';
+ end;
+end;
+
+{*
+ * Unifies an input-device's source name.
+ * Note: the description member of the device must already be set when
+ * calling this function.
+ *}
+function TAudioInputBase.UnifyDeviceSourceName(const name: string; const deviceName: string): string;
+var
+ Descr: string;
+begin
+ result := name;
+
+ {$IFDEF DARWIN}
+ // Under MacOSX the SingStar Mics have an empty
+ // InputName. So, we have to add a hard coded
+ // Workaround for this problem
+ if (name = '') and (Pos( 'USBMIC Serial#', deviceName) > 0) then
+ begin
+ result := 'Microphone';
+ end;
+ {$ENDIF}
+end;
+
+end.
+
+
+