267 lines
5.7 KiB
PHP
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;
|
|
|