diff options
Diffstat (limited to 'Game/Code/Classes/UAudioInput_Bass.pas')
-rw-r--r-- | Game/Code/Classes/UAudioInput_Bass.pas | 542 |
1 files changed, 275 insertions, 267 deletions
diff --git a/Game/Code/Classes/UAudioInput_Bass.pas b/Game/Code/Classes/UAudioInput_Bass.pas index bf0c916e..adc3bf3e 100644 --- a/Game/Code/Classes/UAudioInput_Bass.pas +++ b/Game/Code/Classes/UAudioInput_Bass.pas @@ -1,267 +1,275 @@ -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;
-
- function Init(): boolean;
- function Start(): boolean; 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 }
-
-function TBassInputDevice.Init(): boolean;
-begin
- Result := false;
-
- // 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;
-
- Result := true;
-end;
-
-{*
- * Start input-capturing on this device.
- * TODO: call BASS_RecordInit only once
- *}
-function TBassInputDevice.Start(): boolean;
-var
- flags: Word;
-const
- latency = 20; // 20ms callback period (= latency)
-begin
- Result := false;
-
- // recording already started -> stop first
- if (RecordStream <> 0) then
- Stop();
-
- if not Init() then
- Exit;
-
- case AudioFormat.Format of
- asfS16: flags := 0;
- asfFloat: flags := BASS_SAMPLE_FLOAT;
- asfU8: flags := BASS_SAMPLE_8BITS;
- else begin
- Log.LogError('Unhandled sample-format', 'TBassInputDevice.Start');
- Exit;
- end;
- end;
-
- // start capturing
- RecordStream := BASS_RecordStart(AudioFormat.SampleRate, AudioFormat.Channels,
- MakeLong(flags, latency),
- @MicrophoneCallback, DeviceIndex);
- if (RecordStream = 0) then
- begin
- BASS_RecordFree;
- Exit;
- end;
-
- Result := true;
-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;
- RecordInfo: BASS_RECORDINFO;
-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;
-
- // try to intialize the device
- if not BASS_RecordInit(BassDeviceID) then
- begin
- Log.LogStatus('Failed to initialize BASS Capture-Device['+inttostr(BassDeviceID)+']',
- 'TAudioInput_Bass.InitializeRecord');
- end
- else
- begin
- 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);
-
- // retrieve recording device info
- BASS_RecordGetInfo(RecordInfo);
-
- // FIXME: does BASS use LSB/MSB or system integer values for 16bit?
-
- // check if BASS has capture-freq. info
- if (RecordInfo.freq > 0) then
- begin
- // use current input sample rate (available only on Windows Vista and OSX).
- // Recording at this rate will give the best quality and performance, as no resampling is required.
- BassDevice.AudioFormat := TAudioFormatInfo.Create(2, RecordInfo.freq, asfS16)
- end
- else
- begin
- // BASS does not provide an explizit input channel count (except BASS_RECORDINFO.formats)
- // but it doesn't fail if we use stereo input on a mono device
- // -> use stereo by default
- BassDevice.AudioFormat := TAudioFormatInfo.Create(2, 44100, asfS16)
- end;
-
- SetLength(BassDevice.CaptureChannel, BassDevice.AudioFormat.Channels);
-
- // get input sources
- SourceIndex := 0;
- BassDevice.MicSource := 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 <> -1) and ((Flags and BASS_INPUT_TYPE_MIC) <> 0)) then
- begin
- BassDevice.MicSource := SourceIndex;
- end;
-
- Inc(SourceIndex);
- end;
-
- //Writeln('BASS_RecordFree');
- // FIXME: this call hangs in FPC (windows) every 2nd time USDX is called.
- // Maybe because the sound-device was not released properly?
- BASS_RecordFree;
- //Writeln('BASS_RecordFree - Done');
-
- Inc(DeviceIndex);
- end;
-
- 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.
+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; + + function Init(): boolean; + function Start(): boolean; 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 } + +function TBassInputDevice.Init(): boolean; +begin + Result := false; + + // 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; + + Result := true; +end; + +{* + * Start input-capturing on this device. + * TODO: call BASS_RecordInit only once + *} +function TBassInputDevice.Start(): boolean; +var + flags: Word; +const + latency = 20; // 20ms callback period (= latency) +begin + Result := false; + + // recording already started -> stop first + if (RecordStream <> 0) then + Stop(); + + if not Init() then + Exit; + + case AudioFormat.Format of + asfS16: flags := 0; + asfFloat: flags := BASS_SAMPLE_FLOAT; + asfU8: flags := BASS_SAMPLE_8BITS; + else begin + Log.LogError('Unhandled sample-format', 'TBassInputDevice.Start'); + Exit; + end; + end; + + // start capturing + RecordStream := BASS_RecordStart(AudioFormat.SampleRate, AudioFormat.Channels, + MakeLong(flags, latency), + @MicrophoneCallback, DeviceIndex); + if (RecordStream = 0) then + begin + BASS_RecordFree; + Exit; + end; + + Result := true; +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; + RecordInfo: BASS_RECORDINFO; +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; + + // try to intialize the device + if not BASS_RecordInit(BassDeviceID) then + begin + Log.LogStatus('Failed to initialize BASS Capture-Device['+inttostr(BassDeviceID)+']', + 'TAudioInput_Bass.InitializeRecord'); + end + else + begin + 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); + + // retrieve recording device info + BASS_RecordGetInfo(RecordInfo); + + // FIXME: does BASS use LSB/MSB or system integer values for 16bit? + + // check if BASS has capture-freq. info + if (RecordInfo.freq > 0) then + begin + // use current input sample rate (available only on Windows Vista and OSX). + // Recording at this rate will give the best quality and performance, as no resampling is required. + BassDevice.AudioFormat := TAudioFormatInfo.Create(2, RecordInfo.freq, asfS16) + end + else + begin + // BASS does not provide an explizit input channel count (except BASS_RECORDINFO.formats) + // but it doesn't fail if we use stereo input on a mono device + // -> use stereo by default + BassDevice.AudioFormat := TAudioFormatInfo.Create(2, 44100, asfS16) + end; + + SetLength(BassDevice.CaptureChannel, BassDevice.AudioFormat.Channels); + + // get input sources + SourceIndex := 0; + BassDevice.MicSource := 0; + + // process each input + while true do + begin + SourceName := BASS_RecordGetInputName(SourceIndex); + {$IFDEF DARWIN} + // Patch for SingStar USB-Microphones: + if ((SourceName = nil) and (SourceIndex = 0) and (Pos('Serial#', Descr) > 0)) then + SourceName := 'Microphone' + else + break; + {$ELSE} + if (SourceName = nil) then + break; + {$ENDIF} + + SetLength(BassDevice.Source, SourceIndex+1); + BassDevice.Source[SourceIndex].Name := + UnifyDeviceSourceName(SourceName, BassDevice.Description); + + // set mic index + Flags := BASS_RecordGetInput(SourceIndex); + if ((Flags <> -1) and ((Flags and BASS_INPUT_TYPE_MIC) <> 0)) then + begin + BassDevice.MicSource := SourceIndex; + end; + + Inc(SourceIndex); + end; + + //Writeln('BASS_RecordFree'); + // FIXME: this call hangs in FPC (windows) every 2nd time USDX is called. + // Maybe because the sound-device was not released properly? + BASS_RecordFree; + //Writeln('BASS_RecordFree - Done'); + + Inc(DeviceIndex); + end; + + 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. |