{ $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;