diff options
Diffstat (limited to 'Game/Code/Classes')
-rw-r--r-- | Game/Code/Classes/UAudioDecoder_FFMpeg.pas | 197 | ||||
-rw-r--r-- | Game/Code/Classes/UAudioInput_Bass.pas | 490 | ||||
-rw-r--r-- | Game/Code/Classes/UAudioInput_Portaudio.pas | 331 | ||||
-rw-r--r-- | Game/Code/Classes/UAudioPlayback_Bass.pas | 450 | ||||
-rw-r--r-- | Game/Code/Classes/UAudioPlayback_Portaudio.pas | 608 | ||||
-rw-r--r-- | Game/Code/Classes/UDraw.pas | 20 | ||||
-rw-r--r-- | Game/Code/Classes/UIni.pas | 1599 | ||||
-rw-r--r-- | Game/Code/Classes/UMain.pas | 35 | ||||
-rw-r--r-- | Game/Code/Classes/UMedia_dummy.pas | 474 | ||||
-rw-r--r-- | Game/Code/Classes/UMusic.pas | 915 | ||||
-rw-r--r-- | Game/Code/Classes/URecord.pas | 850 |
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.
+
+
+
|