267 lines
5.7 KiB
PHP

{
$Log
}
const
MAX_CHANNELS = 16; // Maximum number of audio channels/devices
O_RDONLY = 0;
O_WRONLY = 1;
libname = 'libc.so.6';
var
AudioChannels : array[0..MAX_CHANNELS-1] of String;
(* We import libc functions directly to avoid Kylix
Libc unit limitations *)
function __write(fd : Integer; data : Pointer; size : Integer): Integer; cdecl; external libname;
function __read(Handle: Integer; var Buffer; Count: Integer): Integer; cdecl; external libname;
function ioctl(fd : Integer; command : Integer): Integer; varargs; cdecl; external libname;
function open(PathName: PChar; Flags: Integer): Integer; varargs; cdecl; external libname;
function __close(Handle: Integer): Integer; cdecl; external libname;
function GetAudioDeviceInfo(DevID : Integer; OutputDev : Boolean) : TACSDeviceInfo;
begin
Result.DeviceName := '/dev/dsp'+IntToStr(DevID);
Result.DrvVersion := 0;
Result.Formats := [];
Result.Stereo := True;
end;
procedure TStdAudioOut.SetDevice;
begin
if Busy then raise EACSException.Create(strBusy);
if Ch < OutputChannelsCount then FBaseChannel := Ch
else raise EACSException.Create(Format(strChannelnotavailable,[ch]));
end;
procedure TStdAudioOut.Prepare;
var
parm : Integer;
begin
GetMem(FBuffer,FBufferSize);
// No exceptions here!
FInput.Init;
case FInput.BitsPerSample of
8 : parm := AFMT_U8;
16 : parm := AFMT_S16_LE;
end;
_audio_fd := open(PChar(AudioChannels[FBaseChannel]), O_WRONLY);
ioctl(_audio_fd, SNDCTL_DSP_SETFMT, @parm);
parm := FInput.Channels;
ioctl(_audio_fd, SNDCTL_DSP_CHANNELS, @parm);
parm := FInput.SampleRate;
ioctl(_audio_fd, SNDCTL_DSP_SPEED, @parm);
end;
procedure TStdAudioOut.Done;
begin
//TODO:why this raises an exception ?? FreeMem(FBuffer);
__close(_audio_fd);
_audio_fd := -1;
FInput.Flush;
end;
function TStdAudioOut.DoOutput;
var
Len, i, VCoef : Integer;
P : Pointer;
P1 : PACSBuffer8;
P2 : PACSBuffer16;
begin
// No exceptions Here
Result := True;
if not CanOutput then Exit;
Len := 0;
if Abort then
begin
__close(_audio_fd);
Result := False;
Exit;
end;
try
P := @FBuffer[0];
while InputLock do;
InputLock := True;
Len := Finput.GetData(P, FBufferSize);
InputLock := False;
if FVolume < 255 then
begin
VCoef := Round(FVolume/255);
if FInput.BitsPerSample = 16 then
begin
P2 := @FBuffer[0];
for i := 0 to (Len shr 1) -1 do
P2[i] := P2[i]*VCoef;
end else
begin
P1 := @FBuffer[0];
for i := 0 to Len - 1 do
P1[i] := P1[i]*VCoef;
end;
end;
__write(_audio_fd, P, Len);
except
end;
if Len > 0 then Result := True
else Result := False;
end;
constructor TStdAudioOut.Create;
begin
inherited Create(AOwner);
FVolume := 255;
FBufferSize := $8000;
_audio_fd := -1;
end;
destructor TStdAudioOut.Destroy;
begin
if _audio_fd > 0 then __close(_audio_fd);
inherited Destroy;
end;
destructor TStdAudioIn.Destroy;
begin
inherited Destroy;
__close(_audio_fd);
end;
procedure TStdAudioIn.OpenAudio;
begin
if FOpened = 0 then
_audio_fd := open(PChar(AudioChannels[FBaseChannel]), O_RDONLY);
Inc(FOpened);
end;
procedure TStdAudioIn.CloseAudio;
begin
if FOpened = 1 then __close(_audio_fd);
if FOpened > 0 then Dec(FOpened);
end;
function TStdAudioIn.GetBPS;
var
BPS : Integer;
begin
OpenAudio;
BPS := FBPS;
if (BPS in [8, 16]) = False then BPS := 16;
ioctl(_audio_fd, SNDCTL_DSP_SETFMT, @BPS);
FBPS := BPS;
Result := BPS;
CloseAudio;
end;
function TStdAudioIn.GetCh;
var
Ch : Integer;
begin
OpenAudio;
Ch := FChan;
ioctl(_audio_fd, SNDCTL_DSP_CHANNELS, @Ch);
FChan := Ch;
Result := Ch;
CloseAudio;
end;
function TStdAudioIn.GetSR;
var
SR : Integer;
begin
OpenAudio;
SR := FFreq;
ioctl(_audio_fd, SNDCTL_DSP_SPEED, @SR);
FFreq := SR;
Result := SR;
CloseAudio;
end;
procedure TStdAudioIn.Init;
begin
if Busy then raise EACSException.Create(strBusy);
BufEnd := 0;
BufStart := 1;
FPosition := 0;
BufferSize := $8000;
OpenAudio;
FRecBytes := FRecTime * (GetBPS div 8) * GetCh * GetSR;
FBusy := True;
FSize := FRecBytes;
end;
procedure TStdAudioIn.Flush;
begin
CloseAudio;
FBusy := False;
end;
procedure TStdAudioIn.SetDevice;
begin
if Ch > (OutputChannelsCount - 1) then
if not (csDesigning in ComponentState) then
raise EACSException.Create(Format(strChannelnotavailable,[Ch]));
FBaseChannel := Ch;
end;
function TStdAudioIn.GetData;
var
l : Integer;
begin
if not Busy then raise EACSException.Create(strStreamnotOpen);
if FRecBytes >= 0 then
if FPosition >= FRecBytes then
begin
Result := 0;
Exit;
end;
if BufStart > BufEnd then
begin
BufStart := 1;
l := __read(_audio_fd, FBuffer[BufStart], BufferSize);
if l < 1 then
begin
Result := 0;
Exit;
end
else BufEnd := l;
end;
if BufferSize < (BufEnd - BufStart + 1)
then Result := BufferSize
else Result := BufEnd - BufStart + 1;
Move(FBuffer[BufStart], Buffer^, Result);
Inc(BufStart, Result);
Inc(FPosition, Result);
end;
procedure CountChannels;
var
i, fd : Integer;
fname : String;
begin
OutputChannelsCount := 0;
fname := '/dev/dsp0';
fd := open(PChar(fname), O_RDONLY);
if fd < 0 then
begin
// Under ALSA there is no /dev/dsp0 device
fname := '/dev/dsp';
fd := open(PChar(fname), O_RDONLY);
if fd < 0 then Exit;
end;
AudioChannels[OutputChannelsCount] := fname;
__close(fd);
Inc(OutputChannelsCount);
for i := 1 to MAX_CHANNELS - 2 do
begin
fname := '/dev/dsp' + IntToStr(i);
fd := open(PChar(fname), O_RDONLY);
if fd < 0 then Break;
__close(fd);
AudioChannels[OutputChannelsCount] := fname;
Inc(OutputChannelsCount);
end;
end;