unit UAudioPlayback_Bass; interface {$IFDEF FPC} {$MODE Delphi} {$ENDIF} {$I switches.inc} uses Classes, SysUtils, UMusic; implementation uses UIni, UMain, ULog, UAudioCore_Bass, bass; type 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; procedure Close(); override; function GetLoop(): boolean; override; 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: TBassPlaybackStream; function Load(const Filename: string): TBassPlaybackStream; public function GetName: String; {IAudioOutput interface} function InitializePlayback(): boolean; procedure SetVolume(Volume: integer); procedure SetMusicVolume(Volume: integer); procedure SetLoop(Enabled: boolean); function Open(const Filename: string): boolean; // true if succeed procedure Rewind; procedure Play; procedure Pause; //Pause Mod procedure Stop; procedure Close; function Finished: boolean; function Length: real; function GetPosition: real; procedure SetPosition(Time: real); //Equalizer procedure GetFFTData(var data: TFFTData); // Interface for Visualizer function GetPCMData(var data: TPCMData): Cardinal; // Sounds function OpenSound(const Filename: String): TAudioPlaybackStream; procedure PlaySound(stream: TAudioPlaybackStream); procedure StopSound(stream: TAudioPlaybackStream); end; var singleton_AudioPlaybackBass : IAudioPlayback; constructor TBassPlaybackStream.Create(); begin inherited; Reset(); end; constructor TBassPlaybackStream.Create(stream: HSTREAM); begin Create(); Handle := stream; end; procedure TBassPlaybackStream.Reset(); begin Loop := false; if (Handle <> 0) then Bass_StreamFree(Handle); Handle := 0; end; procedure TBassPlaybackStream.Play(); begin BASS_ChannelPlay(Handle, Loop); end; procedure TBassPlaybackStream.Pause(); begin BASS_ChannelPause(Handle); end; procedure TBassPlaybackStream.Stop(); begin BASS_ChannelStop(Handle); end; procedure TBassPlaybackStream.Close(); begin Reset(); end; function TBassPlaybackStream.GetVolume(): integer; begin 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; 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 bytes := BASS_ChannelSeconds2Bytes(Handle, Time); BASS_ChannelSetPosition(Handle, bytes); end; 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 bytes := BASS_ChannelGetLength(Handle); Result := BASS_ChannelBytes2Seconds(Handle, bytes); end; function TBassPlaybackStream.GetStatus(): TStreamStatus; var state: DWORD; begin state := BASS_ChannelIsActive(Handle); case state of BASS_ACTIVE_PLAYING: result := ssPlaying; BASS_ACTIVE_PAUSED: result := ssPaused; BASS_ACTIVE_STALLED: result := ssBlocked; BASS_ACTIVE_STOPPED: result := ssStopped; else result := ssUnknown; end; end; function TBassPlaybackStream.IsLoaded(): boolean; begin Result := (Handle <> 0); end; function TAudioPlayback_Bass.GetName: String; begin result := 'BASS_Playback'; end; function TAudioPlayback_Bass.InitializePlayback(): boolean; var Pet: integer; S: integer; begin result := false; //Log.BenchmarkStart(4); //Log.LogStatus('Initializing Playback Subsystem', 'Music Initialize'); if not BASS_Init(1, 44100, 0, 0, nil) then begin Log.LogError('Could not initialize BASS', 'Error'); Exit; end; //Log.BenchmarkEnd(4); Log.LogBenchmark('--> Bass Init', 4); // config playing buffer //BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 10); //BASS_SetConfig(BASS_CONFIG_BUFFER, 100); result := true; end; function TAudioPlayback_Bass.Load(const Filename: string): TBassPlaybackStream; var L: Integer; stream: HSTREAM; begin Result := nil; //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; Result := TBassPlaybackStream.Create(stream); end; procedure TAudioPlayback_Bass.SetVolume(Volume: integer); 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); end; procedure TAudioPlayback_Bass.SetMusicVolume(Volume: Integer); begin if assigned(MusicStream) then MusicStream.SetVolume(Volume); end; procedure TAudioPlayback_Bass.SetLoop(Enabled: boolean); begin if assigned(MusicStream) then MusicStream.Loop := Enabled; end; function TAudioPlayback_Bass.Open(const Filename: string): boolean; var stream: HSTREAM; begin Result := false; // free old MusicStream if assigned(MusicStream) then MusicStream.Free; MusicStream := Load(Filename); if not assigned(MusicStream) then Exit; //Set Max Volume SetMusicVolume(100); Result := true; end; procedure TAudioPlayback_Bass.Rewind; begin SetPosition(0); end; procedure TAudioPlayback_Bass.Play; begin if assigned(MusicStream) then MusicStream.Play(); end; procedure TAudioPlayback_Bass.Pause; begin if assigned(MusicStream) then MusicStream.Pause(); end; procedure TAudioPlayback_Bass.Stop; begin if assigned(MusicStream) then MusicStream.Stop(); end; procedure TAudioPlayback_Bass.Close; begin if assigned(MusicStream) then MusicStream.Close(); end; function TAudioPlayback_Bass.Length: real; var bytes: integer; begin if assigned(MusicStream) then Result := MusicStream.GetLength() else Result := -1; end; function TAudioPlayback_Bass.GetPosition: real; begin if assigned(MusicStream) then Result := MusicStream.GetPosition() else Result := -1; end; procedure TAudioPlayback_Bass.SetPosition(Time: real); begin if assigned(MusicStream) then MusicStream.SetPosition(Time); end; function TAudioPlayback_Bass.Finished: boolean; begin if assigned(MusicStream) then Result := (MusicStream.GetStatus() = ssStopped) else Result := true; end; // Equalizer procedure TAudioPlayback_Bass.GetFFTData(var data: TFFTData); begin // Get Channel Data Mono and 256 Values BASS_ChannelGetData(MusicStream.Handle, @data, BASS_DATA_FFT512); end; {* * Copies interleaved PCM 16bit uint (maybe fake) stereo samples into data. * Returns the number of frames (= stereo/mono sample) *} function TAudioPlayback_Bass.GetPCMData(var data: TPCMData): Cardinal; var info: BASS_CHANNELINFO; nBytes: DWORD; begin Result := 0; // Get Channel Data Mono and 256 Values BASS_ChannelGetInfo(MusicStream.Handle, info); FillChar(data, sizeof(TPCMData), 0); // no support for non-stereo files at the moment if (info.chans <> 2) then Exit; nBytes := BASS_ChannelGetData(MusicStream.Handle, @data, sizeof(TPCMData)); if(nBytes <= 0) then result := 0 else result := nBytes div sizeof(TPCMStereoSample); end; function TAudioPlayback_Bass.OpenSound(const Filename: string): TAudioPlaybackStream; begin result := Load(Filename); end; procedure TAudioPlayback_Bass.PlaySound(stream: TAudioPlaybackStream); begin if assigned(stream) then stream.Play(); end; procedure TAudioPlayback_Bass.StopSound(stream: TAudioPlaybackStream); begin if assigned(stream) then stream.Stop(); end; initialization singleton_AudioPlaybackBass := TAudioPlayback_Bass.create(); AudioManager.add( singleton_AudioPlaybackBass ); finalization AudioManager.Remove( singleton_AudioPlaybackBass ); end.