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.