lasarus_compotents/acs/Src/drivers/acs_alsaaudio.pas

482 lines
13 KiB
ObjectPascal

(*
this file is a part of audio components suite v 2.3.
copyright (c) 2002-2005 andrei borovsky. all rights reserved.
see the license file for more details.
you can contact me at mail@z0m3ie.de
*)
{
$Log: acs_alsaaudio.pas,v $
Revision 1.6 2006/08/30 18:59:51 z0m3ie
*** empty log message ***
Revision 1.5 2006/07/04 17:12:45 z0m3ie
ACS 2.4 alt wiederhergestellt (unterschiedliche Sampleformate ...)
Revision 1.2 2006/01/01 18:46:40 z0m3ie
*** empty log message ***
Revision 1.1 2005/12/19 18:36:05 z0m3ie
*** empty log message ***
Revision 1.1 2005/09/13 21:53:45 z0m3ie
maked seperat driver (not complete jet)
Revision 1.1 2005/09/12 22:04:52 z0m3ie
modified structure again, fileformats are now in an sperat folder.
all File In/Out classes are capsulated from TFileIn and TFileOut
Revision 1.3 2005/08/28 20:31:17 z0m3ie
linux restructuring for 2.4
Revision 1.2 2005/08/26 17:03:20 z0m3ie
begon to make acs resourcestring aware
more advanced tmixer for windows
restructured tmixer its better handleable now
Revision 1.1 2005/08/25 20:18:00 z0m3ie
Version 2.4 restructure
TCDPlayer removed (fits not in component structure)
TMP3ToWavConverter removed (fits not in component structure)
Revision 1.2 2005/08/22 20:17:01 z0m3ie
changed Headers to log
changed mail adress
}
{$ifdef mswindows}{$message error 'unit not supported'}{$endif}
unit acs_alsaaudio;
interface
uses
Classes, SysUtils, ACS_Types, ACS_Classes, baseunix, alsa, ACS_Strings,ACS_Audio;
const
BUF_SIZE = $4000;
ALSAStateIdle = $ffffffff; // additional DriverState value;
LATENCY = 60;
type
EALSABufferUnderrun = class(EACSException);
EALSABufferOverrun = class(EACSException);
{ TALSAAudioIn }
TALSAAudioIn = class(TACSBaseAudioIn)
private
FDevice : String;
FPeriodSize, FPeriodNum : Integer;
_audio_handle : Psnd_pcm_t;
_hw_params : Psnd_pcm_hw_params_t;
Busy : Boolean;
FBPS, FChan, FFreq : Integer;
BufStart, BufEnd : Integer;
FOpened : Integer;
FRecTime : Integer;
FRecBytes : Integer;
FLatency : Double;
FSilentOnOverrun : Boolean;
function GetDriverState: Integer;
procedure OpenAudio;
procedure CloseAudio;
// function GetDriverState : Integer;
protected
function GetBPS : Integer; override;
function GetCh : Integer; override;
function GetSR : Integer; override;
procedure SetDevice(Ch : Integer);override;
function GetDeviceInfo : TACSDeviceInfo;override;
function GetTotalTime : real; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function GetData(Buffer : Pointer; BufferSize : Integer): Integer; override;
procedure Init; override;
procedure Flush; override;
property DriverState : Integer read GetDriverState;
published
property PeriodSize : Integer read FPeriodSize write FPeriodSize;
property PeriodNum : Integer read FPeriodNum write FPeriodNum;
property SilentOnOverrun : Boolean read FSilentOnOverrun write FSilentOnOverrun;
end;
{ TALSAAudioOut }
TALSAAudioOut = class(TACSBaseAudioOut)
private
FDevice : String;
FPeriodSize, FPeriodNum : Integer;
_audio_handle : Psnd_pcm_t;
_hw_params : Psnd_pcm_hw_params_t;
FBufferSize : Integer;
FVolume : Byte;
_audio_fd : Integer;
Buffer : array [0..BUF_SIZE-1] of Byte;
FLatency : Double;
FSilentOnUnderrun : Boolean;
function GetDriverState : Integer;
protected
procedure Done; override;
function DoOutput(Abort : Boolean):Boolean; override;
procedure Prepare; override;
procedure SetDevice(Ch : Integer);override;
function GetDeviceInfo : TACSDeviceInfo;override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property DriverState : Integer read GetDriverState;
property Latency : Double read FLatency;
published
property BufferSize : Integer read FBufferSize write FBufferSize;
property Device : String read FDevice write FDevice stored True;
property PeriodSize : Integer read FPeriodSize write FPeriodSize;
property PeriodNum : Integer read FPeriodNum write FPeriodNum;
property SilentOnUnderrun : Boolean read FSilentOnUnderrun write FSilentOnUnderrun;
property Volume : Byte read FVolume write FVolume stored True;
end;
implementation
constructor TALSAAudioIn.Create;
begin
inherited Create(AOwner);
if not (csDesigning in ComponentState) then
if not AsoundlibLoaded then
raise EACSException.Create(Format(strcoudntloadLib,[asoundlib_path]));
FBPS := 8;
FChan := 1;
FFreq := 8000;
FSize := -1;
FRecTime := 600;
FDevice := 'default';
BufferSize := 32768;
FSilentOnOverrun := True;
end;
destructor TALSAAudioIn.Destroy;
begin
inherited Destroy;
CloseAudio;
end;
procedure TALSAAudioIn.OpenAudio;
var
Res : Integer;
begin
if FOpened = 0 then
begin
Res := snd_pcm_open(_audio_handle, @FDevice[1], SND_PCM_STREAM_CAPTURE, 0);
if Res < 0 then
raise EACSException.Create(Format(strcoudntopendevice,[FDevice]));
// snd_pcm_reset(_audio_handle);
end;
Inc(FOpened);
end;
procedure TALSAAudioIn.CloseAudio;
begin
if FOpened = 1 then
begin
snd_pcm_drop(_audio_handle);
snd_pcm_close(_audio_handle);
end;
if FOpened > 0 then Dec(FOpened);
end;
(* Note on the following three methods.
These methods simply return the values passed by the user.
As the actual input process begins, ALSAAudioIn may return a bit different
value of the samplerate that is actually set by the ALSA drivers.*)
function TALSAAudioIn.GetBPS : Integer;
begin
Result := FBPS;
end;
function TALSAAudioIn.GetCh : Integer;
begin
Result := FChan;
end;
function TALSAAudioIn.GetSR : Integer;
begin
Result := FFreq;
end;
procedure TALSAAudioIn.SetDevice(Ch: Integer);
begin
FDevice := IntToStr(ch);
end;
function TALSAAudioIn.GetDeviceInfo: TACSDeviceInfo;
begin
end;
procedure TALSAAudioIn.Init;
var
aBufSize : Integer;
begin
if Busy then raise EACSException.Create(strBusy);
BufEnd := 0;
BufStart := 1;
FPosition := 0;
OpenAudio;
snd_pcm_hw_params_malloc(_hw_params);
snd_pcm_hw_params_any(_audio_handle, _hw_params);
snd_pcm_hw_params_set_access(_audio_handle, _hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
if FBPS = 8 then snd_pcm_hw_params_set_format(_audio_handle, _hw_params, SND_PCM_FORMAT_U8)
else snd_pcm_hw_params_set_format(_audio_handle, _hw_params, SND_PCM_FORMAT_S16_LE);
Self.FFreq := snd_pcm_hw_params_set_rate_near(_audio_handle, _hw_params, FFreq, 0);
snd_pcm_hw_params_set_channels(_audio_handle, _hw_params, FChan);
if (FPeriodSize <> 0) and (FPeriodNum <> 0) then
begin
snd_pcm_hw_params_set_period_size_near(_audio_handle, _hw_params, FPeriodSize, 0);
snd_pcm_hw_params_set_periods_near(_audio_handle, _hw_params, FPeriodNum, 0);
aBufSize := (FPeriodSize * FPeriodNum) div (FChan * (FBPS shr 3));
end
else aBufSize := Self.BufferSize div (FChan * (FBPS shr 3));
snd_pcm_hw_params_set_buffer_size_near(_audio_handle, _hw_params, aBufSize);
snd_pcm_hw_params(_audio_handle, _hw_params);
snd_pcm_hw_params_free(_hw_params);
if snd_pcm_prepare(_audio_handle) < 0 then
begin
CloseAudio;
raise EACSException.Create(strInputstartfailed);
end;
try
FLatency := snd_pcm_hw_params_get_period_size(_audio_handle, 0) *
snd_pcm_hw_params_get_periods(_audio_handle, 0)/(FFreq * FChan * (FBPS shr 3));
except
end;
FRecBytes := FRecTime * (GetBPS div 8) * GetCh * GetSR;
Busy := True;
FSize := FRecBytes;
end;
procedure TALSAAudioIn.Flush;
begin
snd_pcm_drain(_audio_handle);
CloseAudio;
Busy := False;
end;
function TALSAAudioIn.GetData(Buffer : Pointer; BufferSize : Integer): Integer;
var
l : Integer;
begin
if not Busy then raise EACSException.Create(strStreamnotopen);
if FPosition >= FRecBytes then
begin
Result := 0;
Exit;
end;
if BufStart > BufEnd then
begin
BufStart := 1;
l := snd_pcm_readi(_audio_handle, @FBuffer[1], (BUF_SIZE div FChan) div (FBPS shr 3));
while l < 0 do
begin
snd_pcm_prepare(_audio_handle);
if not FSilentOnOverrun then
raise EALSABufferOverrun.Create(strBufferoverrun);
l := snd_pcm_readi(_audio_handle, @FBuffer[1], (BUF_SIZE div FChan) div (FBPS shr 3));
end;
if l <> (BUF_SIZE div FChan) div (FBPS shr 3) then
begin
Result := 0;
Exit;
end
else BufEnd := l*FChan*(FBPS shr 3);
end;
if BufferSize < (BufEnd - BufStart + 1)
then Result := BufferSize
else Result := BufEnd - BufStart + 1;
Move(FBuffer[BufStart-1], Buffer^, Result);
Inc(BufStart, Result);
Inc(FPosition, Result);
end;
function TALSAAudioIn.GetTotalTime : real;
begin
Result := FRecTime;
end;
function TALSAAudioIn.GetDriverState : Integer;
begin
if FOpened = 0 then Result := ALSAStateIdle
else Result := snd_pcm_state(_audio_handle);
end;
constructor TALSAAudioOut.Create;
begin
inherited Create(AOwner);
if not (csDesigning in ComponentState) then
if not AsoundlibLoaded then
raise EACSException.Create(Format(strCoudntloadLib,[asoundlib_path]));
FVolume := 255;
FDevice := 'default';
FBufferSize := 32768;
FSilentOnUnderrun := True;
end;
destructor TALSAAudioOut.Destroy;
begin
if _audio_handle <> nil then snd_pcm_close(_audio_handle);
inherited Destroy;
end;
procedure TALSAAudioOut.Prepare;
var
Res, aBufSize : Integer;
begin
FInput.Init;
Res := snd_pcm_open(_audio_handle, @FDevice[1], SND_PCM_STREAM_PLAYBACK, 0);
if Res < 0 then
raise EACSException.Create(Format(strCoudntopendeviceOut,[FDevice]));
//snd_pcm_reset(_audio_handle);
snd_pcm_hw_params_malloc(_hw_params);
snd_pcm_hw_params_any(_audio_handle, _hw_params);
snd_pcm_hw_params_set_access(_audio_handle, _hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
if FInput.BitsPerSample = 8 then
snd_pcm_hw_params_set_format(_audio_handle, _hw_params, SND_PCM_FORMAT_U8)
else
snd_pcm_hw_params_set_format(_audio_handle, _hw_params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_rate_near(_audio_handle, _hw_params, FInput.SampleRate, 0);
snd_pcm_hw_params_set_channels(_audio_handle, _hw_params, FInput.Channels);
if (FPeriodSize <> 0) and (FPeriodNum <> 0) then
begin
snd_pcm_hw_params_set_period_size_near(_audio_handle, _hw_params, FPeriodSize, 0);
snd_pcm_hw_params_set_periods_near(_audio_handle, _hw_params, FPeriodNum, 0);
aBufSize := (FPeriodSize * FPeriodNum) div (Finput.Channels * (Finput.BitsPerSample shr 3));
end
else aBufSize := Self.FBufferSize div (Finput.Channels * (Finput.BitsPerSample shr 3));
snd_pcm_hw_params_set_buffer_size_near(_audio_handle, _hw_params, aBufSize);
snd_pcm_hw_params(_audio_handle, _hw_params);
snd_pcm_hw_params_free(_hw_params);
if snd_pcm_prepare(_audio_handle) < 0 then
begin
raise EACSException.Create(strFailedtostartoutput);
end;
try
FLatency := snd_pcm_hw_params_get_period_size(_audio_handle, 0) *
snd_pcm_hw_params_get_periods(_audio_handle, 0)/(Finput.Channels * (Finput.BitsPerSample shr 3));
except
end;
end;
procedure TALSAAudioOut.SetDevice(Ch: Integer);
begin
// FDevice := IntToStr(ch);
end;
function TALSAAudioOut.GetDeviceInfo: TACSDeviceInfo;
begin
end;
procedure TALSAAudioOut.Done;
begin
snd_pcm_drain(_audio_handle);
snd_pcm_close(_audio_handle);
_audio_handle := 0;
FInput.Flush;
end;
function TALSAAudioOut.DoOutput(Abort : Boolean):Boolean;
var
Len, i, VCoef, l : Integer;
P : Pointer;
P1 : PACSBuffer8;
P2 : PACSBuffer16;
begin
// No exceptions Here
Result := True;
if not CanOutput then Exit;
Len := 0;
if Abort then
begin
snd_pcm_drain(_audio_handle);
snd_pcm_close(_audio_handle);
_audio_handle := 0;
Result := False;
Exit;
end;
try
P := @Buffer[0];
while InputLock do;
InputLock := True;
Len := Finput.GetData(P, BUF_SIZE);
InputLock := False;
if Len = 0 then
begin
Result := False;
Exit;
end;
if FVolume < 255 then
begin
VCoef := Round(FVolume/255);
if FInput.BitsPerSample = 16 then
begin
P2 := @Buffer[0];
for i := 0 to (Len shr 1) -1 do
P2[i] := P2[i]*VCoef;
end else
begin
P1 := @Buffer[0];
for i := 0 to Len - 1 do
P1[i] := P1[i]*VCoef;
end;
end;
l := snd_pcm_writei(_audio_handle, P, (Len div Finput.Channels) div (FInput.BitsPerSample shr 3));
while l < 0 do
begin
snd_pcm_prepare(_audio_handle);
if not FSilentOnUnderrun then
raise EALSABufferUnderrun.Create(strBufferunderrun);
l := snd_pcm_writei(_audio_handle, P, (Len div Finput.Channels) div (FInput.BitsPerSample shr 3));
end;
if l = (Len div Finput.Channels) div (FInput.BitsPerSample shr 3)
then Result := True
else Result := False;
except
end;
end;
function TALSAAudioOut.GetDriverState : Integer;
begin
if not Busy then Result := ALSAStateIdle
else Result := snd_pcm_state(_audio_handle);
end;
initialization
if AsoundlibLoaded then
begin
RegisterAudioOut('Alsa',TAlsaAudioOut,LATENCY);
RegisterAudioIn('Alsa',TAlsaAudioIn,LATENCY);
end;
end.