aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code/Classes
diff options
context:
space:
mode:
authortobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-02-05 20:36:19 +0000
committertobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-02-05 20:36:19 +0000
commitdee94f5dae9e6b5ae6c7b54a12a567745abc8dc3 (patch)
tree21767870a9f01fe8df91fd3ce5331e2fe89862fc /Game/Code/Classes
parent2e6592ceadb3e3c910164c76595e7ae435b8823a (diff)
downloadusdx-dee94f5dae9e6b5ae6c7b54a12a567745abc8dc3.tar.gz
usdx-dee94f5dae9e6b5ae6c7b54a12a567745abc8dc3.tar.xz
usdx-dee94f5dae9e6b5ae6c7b54a12a567745abc8dc3.zip
General:
- cleanup and adaption of SingDrawOscilloscope Portaudio/SDL audio output: - stuttering in portaudio output has been fixed (SDL_MixBuffers cannot be used without initializing the SDL audio stuff first, so it is not usable with portaudio. Now SDL is used for audio-output instead of portaudio (although the file-name is UAudioPlayback_Portaudio.pas at the moment). - cleaner file closing - volume adjustment UMusic: - cleanup of the audio-interfaces - introduced TNoteType = (ntFreestyle, ntNormal, ntGolden) - some bug-fixes - introduced TSoundLibrary. This is library for all in-game sounds used by USDX. Instead of calling AudioPlayer.PlaySwoosh you should call AudioPlayer.PlaySound(SoundLib.Swoosh) now. You might call SoundLib.Swoosh.Play too, but this is not recommended at the moment because SoundLib.Swoosh could be nil if the file was not found. The SoundLibrary approach is much cleaner than the previous one. The AudioPlayer does not have to specify a Play... and Stop... method for every available sound anymore. In addition it is not an AudioPlayers responsibility to init the in-game sounds. URecord: - polish to english translation of some variables - CaptureSoundLeft/Right is CaptureChannel[0/1] now - TSoundCardInput -> TAudioInputDeviceSource - TGenericSoundCard.Input -> TGenericSoundCard.Source - autocorrelation algorithm more readable now - Clean-up of the audio-input interface - moved cloned code of the input-classes to one base class (TAudioInputBase) - Cleaner finalization - Start-/StopCapture will not crash anymore in the recording-options menu - Fixed several bugs in the autocorrelation stuff (e.g. wrong usage of $10000) - SzczytJest (now ToneValid) was not used correctly. ToneValid is set to true if a valid tone was found (= the sound was louder than the threshold -> no background noise). If i remember correctly the sound was accepted although the tone was invalid. So the old data was used although noone was singing. This resulted in some sort of ghost-singer effect. UIni: - moved TIni.Card to TScreenOptionsRecord.Card because it is not stored in the ini-file and will not be in the future. - TIni.CardList ist now TIni.InputDeviceConfig. The name cardlist was misleading because it just specifies input- but no output-devices. In addition a soundcard can have multiple input-devices (at least in linux). - bugfix on InputDeviceConfig (formerly CardList) usage. USDX expected that the indices of the corresponding elements in TIni.InputDeviceConfig[] and TAudioInputProcessor.Device[] were the same. This is wrong. If device 2 was defined at first place in the ini and device 1 at the second, the indices of the two arrays didn't match (they were swapped) erroneously. To fix this and to support the item listed below the index to TIni.InputDeviceConfig[] is now stored in TAudioInputDevice.CfgIndex. NOTE: InputDeviceConfig[] contains configurations of non-available (unplugged) devices. Iterate over TAudioInputProcessor.Device[] for available devices. - configurations of external devices that are not plugged in will not be deleted anymore. - multiple definitions of one device in the ini-file will not crash USDX anymore - CardList[I].ChannelL/R now are InputDeviceConfig[I].ChannelToPlayerMap[0/1]. I think the new name is more intuitive because it maps a channel to a player number. Now the both vars are joint to one array. Now it is possible to use loops to process them and we might support more than two input channels on one device in the future (if such devices exist) git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@827 b956fd51-792f-4845-bead-9b4dfca2ff2c
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.
+
+
+