{ $Log: acs_audio.inc,v $ Revision 1.7 2006/08/31 20:10:56 z0m3ie *** empty log message *** Revision 1.6 2006/07/04 17:12:45 z0m3ie ACS 2.4 alt wiederhergestellt (unterschiedliche Sampleformate ...) Revision 1.3 2006/01/01 18:46:40 z0m3ie *** empty log message *** Revision 1.2 2005/12/26 17:31:39 z0m3ie fixed some problems in acs_dsfiles fixed some problems in acs_vorbis reworked all buffers Revision 1.1 2005/12/19 18:36:26 z0m3ie *** empty log message *** Revision 1.6 2005/12/18 17:01:54 z0m3ie delphi compatibility Revision 1.5 2005/12/04 16:54:34 z0m3ie All classes are renamed, Style TACS... than T... to avoid conflicts with other components (eg TMixer is TACSMixer now) Revision 1.4 2005/11/27 16:50:34 z0m3ie add ACS VolumeQuerry make ACS_VolumeQuerry localizeable some little errorfixes (buffersize for linuxdrivers was initially 0) make TAudioIn workable Revision 1.3 2005/10/02 16:51:31 z0m3ie *** empty log message *** Revision 1.2 2005/09/18 19:29:00 z0m3ie more progress on driver handling } function GetAudioDeviceInfo(DevID : Integer; OutputDev : Boolean) : TACSDeviceInfo; var WIC : TWaveInCaps; i : Integer; begin if OutputDev then begin if DevID >= OutputChannelsCount then raise EACSException.Create(Format(strChannelnotavailable,[DevId])); end else begin if DevID >= InputChannelsCount then raise EACSException.Create(Format(strChannelnotavailable,[DevId])); end; if OutputDev then waveOutGetDevCaps(DevID, @WIC, SizeOf(WIC)) else waveInGetDevCaps(DevID, @WIC, SizeOf(WIC)); i := 0; while WIC.szPname[i] <> #0 do Inc(i); SetLength(Result.DeviceName, i); Move(WIC.szPname[0], Result.DeviceName[1], i); Result.Formats := []; if (WIC.dwFormats and WAVE_FORMAT_1M08) <> 0 then Result.Formats := Result.Formats + [af1M08]; if (WIC.dwFormats and WAVE_FORMAT_1M16) <> 0 then Result.Formats := Result.Formats + [af1M16]; if (WIC.dwFormats and WAVE_FORMAT_1S08) <> 0 then Result.Formats := Result.Formats + [af1S08]; if (WIC.dwFormats and WAVE_FORMAT_1S16) <> 0 then Result.Formats := Result.Formats + [af1S16]; if (WIC.dwFormats and WAVE_FORMAT_2M08) <> 0 then Result.Formats := Result.Formats + [af2M08]; if (WIC.dwFormats and WAVE_FORMAT_2M16) <> 0 then Result.Formats := Result.Formats + [af2M16]; if (WIC.dwFormats and WAVE_FORMAT_2S08) <> 0 then Result.Formats := Result.Formats + [af2S08]; if (WIC.dwFormats and WAVE_FORMAT_2S16) <> 0 then Result.Formats := Result.Formats + [af2S16]; if (WIC.dwFormats and WAVE_FORMAT_4M08) <> 0 then Result.Formats := Result.Formats + [af4M08]; if (WIC.dwFormats and WAVE_FORMAT_4M16) <> 0 then Result.Formats := Result.Formats + [af4M16]; if (WIC.dwFormats and WAVE_FORMAT_4S08) <> 0 then Result.Formats := Result.Formats + [af4S08]; if (WIC.dwFormats and WAVE_FORMAT_4S16) <> 0 then Result.Formats := Result.Formats + [af4S16]; Result.DrvVersion := WIC.vDriverVersion; if WIC.wChannels = 1 then Result.Stereo := False else Result.Stereo := True; end; procedure WaveOutProc(hwo, Msg : LongWord; Instance : Pointer; Param1, Param2 : LongWord); stdcall; var Audio : TStdAudioOut; begin EnterCriticalSection(CrSecO); if Msg = WOM_DONE then begin Audio := TStdAudioOut(Instance); Audio.AddBlockToChain(PWaveHdr(Param1)); end; LeaveCriticalSection(CrSecO); end; procedure WaveInProc(hwi, Msg : LongWord; Instance : Pointer; Param1, Param2 : LongWord); stdcall; var Audio : TStdAudioIn; begin EnterCriticalSection(CrSecI); if Msg = WIM_DATA then begin Audio := TStdAudioIn(Instance); Audio.AddBlockToChain(PWaveHdr(Param1)); end; LeaveCriticalSection(CrSecI); end; procedure TStdAudioOut.AddBlockToChain(WH : PWaveHdr); begin WH.lpNext := nil; EOC^ := WH; EOC := @WH.lpNext; Dec(aBlock); end; procedure TStdAudioOut.SetDevice; begin if Busy then raise EACSException.Create(strBusy); if OutputChannelsCount = 0 then FBaseChannel := 0 else if Ch < OutputChannelsCount then FBaseChannel := Ch else raise EACSException.Create(Format(strChannelnotavailable,[Ch])); end; procedure TStdAudioOut.Prepare; var WF : TPCMWaveFormat; begin // No exceptions here! FInput.Init; WF.wf.wFormatTag := WAVE_FORMAT_PCM; WF.wf.nChannels := FInput.Channels; WF.wf.nSamplesPerSec := FInput.SampleRate; WF.wBitsPerSample := FInput.BitsPerSample; WF.wf.nAvgBytesPerSec := WF.wf.nSamplesPerSec*WF.wBitsPerSample div 8; WF.wf.nBlockAlign := WF.wf.nChannels * WF.wBitsPerSample div 8; waveOutOpen(@_audio_fd, FBaseChannel, @WF, DWORD(@WaveOutProc), DWORD(Self), CALLBACK_FUNCTION or WAVE_MAPPED); aBlock := 0; FBuffer := AllocMem(FBufferSize); EOC := @BlockChain; end; procedure TStdAudioOut.Done; var Tmp : PWaveHdr; begin if _audio_fd <> -1 then begin while aBlock > 0 do; Tmp := BlockChain; while Tmp <> nil do begin BlockChain := Tmp.lpNext; waveOutUnprepareHeader(_audio_fd, Tmp, SizeOf(TWaveHdr)); FreeMem(Tmp.lpData); Dispose(Tmp); Tmp := BlockChain; end; EOC := @BlockChain; waveOutClose(_audio_fd); FreeMem(FBuffer); _audio_fd := -1; end; FInput.Flush; end; function TStdAudioOut.DoOutput(Abort : Boolean):Boolean; var Len, i, k, vCoef : Integer; P : Pointer; P1 : PACSBuffer8; P2 : PACSBuffer16; Tmp : PWaveHdr; begin // No exceptions Here Result := True; if not Busy then Exit; if Abort or (not CanOutput) then begin Result := False; Exit; end; Tmp := BlockChain; // clear pending data blocks while Tmp <> nil do begin BlockChain := Tmp.lpNext; waveOutUnprepareHeader(_audio_fd, Tmp, SizeOf(TWaveHdr)); FreeMem(Tmp.lpData); Dispose(Tmp); Tmp := BlockChain; end; EOC := @BlockChain; (* Write more than one block. This is needed for audio sources like Vorbis codec that return data in small chunks. *) for k := aBlock to FReadChunks do begin GetMem(P, FBufferSize div FReadChunks); while InputLock do; InputLock := True; Len := Finput.GetData(P, FBufferSize div FReadChunks); InputLock := False; if Len > 0 then Result := True else begin Result := False; FreeMem(P); Exit; end; if FVolume < 255 then begin vCoef := Round(FVolume/255); if FInput.BitsPerSample = 16 then begin P2 := P; for i := 0 to (Len shr 1) -1 do P2[i] := P2[i]*vCoef; end else begin P1 := P; for i := 0 to Len - 1 do P1[i] := P1[i]*vCoef; end; end; WriteBlock(P, Len); end; end; constructor TStdAudioOut.Create; begin inherited Create(AOwner); FBaseChannel := 0; FVolume := 255; _audio_fd := -1; Delay := 6; FReadChunks := 8; FBufferSize := $8000; end; destructor TStdAudioOut.Destroy; begin if _audio_fd <> -1 then WaveOutClose(_audio_fd); inherited Destroy; end; destructor TStdAudioIn.Destroy; begin waveInClose(_audio_fd); inherited Destroy; end; procedure TStdAudioIn.OpenAudio; var WF : TPCMWaveFormat; begin WF.wf.wFormatTag := WAVE_FORMAT_PCM; WF.wf.nChannels := FChan; WF.wf.nSamplesPerSec := FFreq; WF.wBitsPerSample := FBPS; WF.wf.nAvgBytesPerSec := WF.wf.nSamplesPerSec*WF.wBitsPerSample div 8; WF.wf.nBlockAlign := WF.wf.nChannels * WF.wBitsPerSample div 8; if FOpened = 0 then begin waveInOpen(@_audio_fd, FBaseChannel, @WF, DWORD(@WaveInProc), DWORD(Self), CALLBACK_FUNCTION or WAVE_MAPPED); end; Inc(FOpened); end; procedure TStdAudioIn.CloseAudio; begin if FOpened = 1 then begin waveInClose(_audio_fd); FreeMem(FBuffer); end; if FOpened > 0 then Dec(FOpened); end; function TStdAudioIn.GetBPS : Integer; begin Result := FBPS; end; function TStdAudioIn.GetCh : Integer; begin Result := FChan; end; function TStdAudioIn.GetSR : Integer; begin Result := FFreq; end; procedure TStdAudioIn.Init; begin if Busy then raise EACSException.Create(strBusy); BufEnd := 0; BufStart := 1; FPosition := 0; FRecBytes := FRecTime * (GetBPS div 8) * GetCh * GetSR; FBusy := True; OpenAudio; waveInStart(_audio_fd); BlockChain := nil; FSize := FRecBytes; aBlock := 0; EOC := @BlockChain; end; procedure TStdAudioIn.Flush; var Tmp : PWaveHdr; begin while aBlock > 0 do; // wait until pending data blocks are put to the chain waveInReset(_audio_fd); // return all pending data blocks sleep(10); Tmp := BlockChain; // clear pending data blocks while Tmp <> nil do begin BlockChain := Tmp.lpNext; waveInUnprepareHeader(_audio_fd, Tmp, SizeOf(TWaveHdr)); FreeMem(Tmp.lpData); Dispose(Tmp); Tmp := BlockChain; end; CloseAudio; FBusy := False; end; procedure TStdAudioIn.SetDevice; begin if Busy then raise EACSException.Create(strBusy); if Ch < InputChannelsCount then FBaseChannel := Ch else raise EACSException.Create(Format(strChannelnotavailable,[Ch])); end; function TStdAudioIn.GetData(Buffer : Pointer; oBufferSize : Integer): Integer; var Tmp : PWaveHdr; begin if not Busy then raise EACSException.Create(strStreamnotopen); if FRecBytes >= 0 then if (FPosition >= FRecBytes) then begin Result := 0; Exit; end; while aBlock < FBlocksCount do NewBlock; if BufStart > BufEnd then begin BufStart := 1; while BlockChain = nil do sleep(10); TMP := BlockChain; BlockChain := BlockChain.lpNext; if BlockChain = nil then EOC := @BlockChain; Move(Tmp.lpData[0], FBuffer[1], Tmp.dwBytesRecorded); BufEnd := Tmp.dwBytesRecorded; waveInUnprepareHeader(_audio_fd, Tmp, SizeOf(TWaveHdr)); FreeMem(Tmp.lpData); Dispose(Tmp); 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 TStdAudioOut.WriteBlock; var WH : PWaveHdr; begin Inc(aBlock); New(WH); WH.lpData := P; WH.dwBufferLength := Len; WH.dwLoops := 0; WH.dwFlags := 0; waveOutPrepareHeader(_audio_fd, WH, SizeOf(TWaveHdr)); waveOutWrite(_audio_fd, WH, SizeOf(TWaveHdr)); end; procedure TStdAudioIn.NewBlock; var WH : PWaveHdr; begin New(WH); GetMem(WH.lpData, BufferSize div FBlocksCount); WH.dwBufferLength := BufferSize div FBlocksCount; WH.dwFlags := 0; waveInPrepareHeader(_audio_fd, WH, SizeOf(TWaveHdr)); waveInAddBuffer(_audio_fd, WH, SizeOf(TWaveHdr)); Inc(aBlock); end; function CountChannels : Integer; begin OutputChannelsCount := waveOutGetNumDevs; InputChannelsCount := waveInGetNumDevs; end; procedure TStdAudioIn.AddBlockToChain(WH : PWaveHdr); begin WH.lpNext := nil; EOC^ := WH; EOC := @WH.lpNext; Dec(aBlock); end;