aboutsummaryrefslogtreecommitdiffstats
path: root/src/classes0/UAudioDecoder_Bass.pas
diff options
context:
space:
mode:
Diffstat (limited to 'src/classes0/UAudioDecoder_Bass.pas')
-rw-r--r--src/classes0/UAudioDecoder_Bass.pas242
1 files changed, 242 insertions, 0 deletions
diff --git a/src/classes0/UAudioDecoder_Bass.pas b/src/classes0/UAudioDecoder_Bass.pas
new file mode 100644
index 00000000..dba1fde4
--- /dev/null
+++ b/src/classes0/UAudioDecoder_Bass.pas
@@ -0,0 +1,242 @@
+unit UAudioDecoder_Bass;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+implementation
+
+uses
+ Classes,
+ SysUtils,
+ UMain,
+ UMusic,
+ UAudioCore_Bass,
+ ULog,
+ bass;
+
+type
+ TBassDecodeStream = class(TAudioDecodeStream)
+ private
+ Handle: HSTREAM;
+ FormatInfo : TAudioFormatInfo;
+ Error: boolean;
+ public
+ constructor Create(Handle: HSTREAM);
+ destructor Destroy(); override;
+
+ procedure Close(); override;
+
+ function GetLength(): real; override;
+ function GetAudioFormatInfo(): TAudioFormatInfo; override;
+ function GetPosition: real; override;
+ procedure SetPosition(Time: real); override;
+ function GetLoop(): boolean; override;
+ procedure SetLoop(Enabled: boolean); override;
+ function IsEOF(): boolean; override;
+ function IsError(): boolean; override;
+
+ function ReadData(Buffer: PChar; BufSize: integer): integer; override;
+ end;
+
+type
+ TAudioDecoder_Bass = class( TInterfacedObject, IAudioDecoder )
+ public
+ function GetName: string;
+
+ function InitializeDecoder(): boolean;
+ function FinalizeDecoder(): boolean;
+ function Open(const Filename: string): TAudioDecodeStream;
+ end;
+
+var
+ BassCore: TAudioCore_Bass;
+
+
+{ TBassDecodeStream }
+
+constructor TBassDecodeStream.Create(Handle: HSTREAM);
+var
+ ChannelInfo: BASS_CHANNELINFO;
+ Format: TAudioSampleFormat;
+begin
+ inherited Create();
+ Self.Handle := Handle;
+
+ // setup format info
+ if (not BASS_ChannelGetInfo(Handle, ChannelInfo)) then
+ begin
+ raise Exception.Create('Failed to open decode-stream');
+ end;
+ BassCore.ConvertBASSFlagsToAudioFormat(ChannelInfo.flags, Format);
+ FormatInfo := TAudioFormatInfo.Create(ChannelInfo.chans, ChannelInfo.freq, format);
+
+ Error := false;
+end;
+
+destructor TBassDecodeStream.Destroy();
+begin
+ Close();
+ inherited;
+end;
+
+procedure TBassDecodeStream.Close();
+begin
+ if (Handle <> 0) then
+ begin
+ BASS_StreamFree(Handle);
+ Handle := 0;
+ end;
+ PerformOnClose();
+ FreeAndNil(FormatInfo);
+ Error := false;
+end;
+
+function TBassDecodeStream.GetAudioFormatInfo(): TAudioFormatInfo;
+begin
+ Result := FormatInfo;
+end;
+
+function TBassDecodeStream.GetLength(): real;
+var
+ bytes: QWORD;
+begin
+ bytes := BASS_ChannelGetLength(Handle, BASS_POS_BYTE);
+ Result := BASS_ChannelBytes2Seconds(Handle, bytes);
+end;
+
+function TBassDecodeStream.GetPosition: real;
+var
+ bytes: QWORD;
+begin
+ bytes := BASS_ChannelGetPosition(Handle, BASS_POS_BYTE);
+ Result := BASS_ChannelBytes2Seconds(Handle, bytes);
+end;
+
+procedure TBassDecodeStream.SetPosition(Time: real);
+var
+ bytes: QWORD;
+begin
+ bytes := BASS_ChannelSeconds2Bytes(Handle, Time);
+ BASS_ChannelSetPosition(Handle, bytes, BASS_POS_BYTE);
+end;
+
+function TBassDecodeStream.GetLoop(): boolean;
+var
+ flags: DWORD;
+begin
+ // retrieve channel flags
+ flags := BASS_ChannelFlags(Handle, 0, 0);
+ if (flags = DWORD(-1)) then
+ begin
+ Log.LogError('BASS_ChannelFlags: ' + BassCore.ErrorGetString(), 'TBassDecodeStream.GetLoop');
+ Result := false;
+ Exit;
+ end;
+ Result := (flags and BASS_SAMPLE_LOOP) <> 0;
+end;
+
+procedure TBassDecodeStream.SetLoop(Enabled: boolean);
+var
+ flags: DWORD;
+begin
+ // set/unset loop-flag
+ if (Enabled) then
+ flags := BASS_SAMPLE_LOOP
+ else
+ flags := 0;
+
+ // set new flag-bits
+ if (BASS_ChannelFlags(Handle, flags, BASS_SAMPLE_LOOP) = DWORD(-1)) then
+ begin
+ Log.LogError('BASS_ChannelFlags: ' + BassCore.ErrorGetString(), 'TBassDecodeStream.SetLoop');
+ Exit;
+ end;
+end;
+
+function TBassDecodeStream.IsEOF(): boolean;
+begin
+ Result := (BASS_ChannelIsActive(Handle) = BASS_ACTIVE_STOPPED);
+end;
+
+function TBassDecodeStream.IsError(): boolean;
+begin
+ Result := Error;
+end;
+
+function TBassDecodeStream.ReadData(Buffer: PChar; BufSize: integer): integer;
+begin
+ Result := BASS_ChannelGetData(Handle, Buffer, BufSize);
+ // check error state (do not handle EOF as error)
+ if ((Result = -1) and (BASS_ErrorGetCode() <> BASS_ERROR_ENDED)) then
+ Error := true
+ else
+ Error := false;
+end;
+
+
+{ TAudioDecoder_Bass }
+
+function TAudioDecoder_Bass.GetName: String;
+begin
+ result := 'BASS_Decoder';
+end;
+
+function TAudioDecoder_Bass.InitializeDecoder(): boolean;
+begin
+ BassCore := TAudioCore_Bass.GetInstance();
+ Result := true;
+end;
+
+function TAudioDecoder_Bass.FinalizeDecoder(): boolean;
+begin
+ Result := true;
+end;
+
+function TAudioDecoder_Bass.Open(const Filename: string): TAudioDecodeStream;
+var
+ Stream: HSTREAM;
+ ChannelInfo: BASS_CHANNELINFO;
+ FileExt: string;
+begin
+ Result := nil;
+
+ // check if BASS was initialized
+ // in case the decoder is not used with BASS playback, init the NO_SOUND device
+ if ((integer(BASS_GetDevice) = -1) and (BASS_ErrorGetCode() = BASS_ERROR_INIT)) then
+ BASS_Init(0, 44100, 0, 0, nil);
+
+ // TODO: use BASS_STREAM_PRESCAN for accurate seeking in VBR-files?
+ // disadvantage: seeking will slow down.
+ Stream := BASS_StreamCreateFile(False, PChar(Filename), 0, 0, BASS_STREAM_DECODE);
+ if (Stream = 0) then
+ begin
+ //Log.LogError(BassCore.ErrorGetString(), 'TAudioDecoder_Bass.Open');
+ Exit;
+ end;
+
+ // check if BASS opened some erroneously recognized file-formats
+ if BASS_ChannelGetInfo(Stream, channelInfo) then
+ begin
+ fileExt := ExtractFileExt(Filename);
+ // BASS opens FLV-files (maybe others too) although it cannot handle them.
+ // Setting BASS_CONFIG_VERIFY to the max. value (100000) does not help.
+ if ((fileExt = '.flv') and (channelInfo.ctype = BASS_CTYPE_STREAM_MP1)) then
+ begin
+ BASS_StreamFree(Stream);
+ Exit;
+ end;
+ end;
+
+ Result := TBassDecodeStream.Create(Stream);
+end;
+
+
+initialization
+ MediaManager.Add(TAudioDecoder_Bass.Create);
+
+end.