mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
start work tx
This commit is contained in:
+148
-26
@@ -56,10 +56,25 @@ type
|
||||
statusFlags: LongWord;
|
||||
userData: Pointer): LongInt; cdecl;
|
||||
|
||||
TPaDeviceInfo = record
|
||||
structVersion: Integer;
|
||||
name: PAnsiChar;
|
||||
hostApi: Integer;
|
||||
maxInputChannels: Integer;
|
||||
maxOutputChannels: Integer;
|
||||
defaultLowInputLatency: TPaTime;
|
||||
defaultLowOutputLatency: TPaTime;
|
||||
defaultHighInputLatency: TPaTime;
|
||||
defaultHighOutputLatency:TPaTime;
|
||||
defaultSampleRate: Double;
|
||||
end;
|
||||
PPaDeviceInfo = ^TPaDeviceInfo;
|
||||
|
||||
TPa_Initialize = function: TPaError; cdecl;
|
||||
TPa_Terminate = function: TPaError; cdecl;
|
||||
TPa_GetDefaultOutputDevice = function: TPaDeviceIndex; cdecl;
|
||||
TPa_GetDeviceInfo = function(device: TPaDeviceIndex): Pointer; cdecl;
|
||||
TPa_GetDeviceCount = function: TPaDeviceIndex; cdecl;
|
||||
TPa_GetDeviceInfo = function(device: TPaDeviceIndex): PPaDeviceInfo; cdecl;
|
||||
TPa_OpenStream = function(stream: PPaStream;
|
||||
inputParam: PPaStreamParameters;
|
||||
outputParam: PPaStreamParameters;
|
||||
@@ -89,10 +104,13 @@ type
|
||||
FOutPt: Integer; // audio_buffer_outpt (read)
|
||||
FMutex: TCriticalSection;
|
||||
|
||||
FDeviceIndex: Integer; // -1 = default output device
|
||||
// PortAudio functions
|
||||
FPa_Initialize: TPa_Initialize;
|
||||
FPa_Terminate: TPa_Terminate;
|
||||
FPa_GetDefaultOutputDevice: TPa_GetDefaultOutputDevice;
|
||||
FPa_GetDeviceCount: TPa_GetDeviceCount;
|
||||
FPa_GetDeviceInfo: TPa_GetDeviceInfo;
|
||||
FPa_OpenStream: TPa_OpenStream;
|
||||
FPa_StartStream: TPa_StartStream;
|
||||
FPa_StopStream: TPa_StopStream;
|
||||
@@ -106,16 +124,28 @@ type
|
||||
constructor Create(SampleRate: Integer = 48000);
|
||||
destructor Destroy; override;
|
||||
|
||||
function Initialize: Boolean; // загружает PA, вызывает Pa_Initialize (без открытия стрима)
|
||||
function Open: Boolean;
|
||||
procedure Close;
|
||||
|
||||
// Перечисление устройств вывода: заполняет Names списком имён.
|
||||
// Возвращает True если PA инициализирована и устройства доступны.
|
||||
// Indices[i] — индекс PA устройства для Names[i].
|
||||
function EnumOutputDevices(Names: TStrings; out Indices: array of Integer;
|
||||
out Count: Integer): Boolean;
|
||||
|
||||
// Найти индекс устройства по имени (-1 если не найдено)
|
||||
function FindDeviceByName(const AName: string): Integer;
|
||||
|
||||
// Пишем стерео double сэмплы — как audio_write() в оригинале
|
||||
procedure WriteDouble(Left, Right: Double);
|
||||
// Convenience: массив Single
|
||||
procedure Write(const Left, Right: array of Single; Count: Integer);
|
||||
|
||||
property IsOpen: Boolean read FOpen;
|
||||
property LastError: string read GetLastError;
|
||||
property IsOpen: Boolean read FOpen;
|
||||
property LastError: string read GetLastError;
|
||||
property DeviceIndex: Integer read FDeviceIndex write FDeviceIndex;
|
||||
property SampleRate: Integer read FSampleRate write FSampleRate;
|
||||
end;
|
||||
|
||||
// Глобальный callback (cdecl, не метод)
|
||||
@@ -202,12 +232,13 @@ end;
|
||||
constructor TAudioOutput.Create(SampleRate: Integer);
|
||||
begin
|
||||
inherited Create;
|
||||
FSampleRate := SampleRate;
|
||||
FOpen := False;
|
||||
FPAInited := False;
|
||||
FStream := nil;
|
||||
FLibHandle := NilHandle;
|
||||
FLastError := '';
|
||||
FSampleRate := SampleRate;
|
||||
FDeviceIndex := -1; // -1 = default output device
|
||||
FOpen := False;
|
||||
FPAInited := False;
|
||||
FStream := nil;
|
||||
FLibHandle := NilHandle;
|
||||
FLastError := '';
|
||||
FInPt := 0;
|
||||
FOutPt := 0;
|
||||
FillChar(FBuf, SizeOf(FBuf), 0);
|
||||
@@ -258,6 +289,8 @@ begin
|
||||
FPa_Initialize := TPa_Initialize(GetProcAddress(FLibHandle, 'Pa_Initialize'));
|
||||
FPa_Terminate := TPa_Terminate(GetProcAddress(FLibHandle, 'Pa_Terminate'));
|
||||
FPa_GetDefaultOutputDevice := TPa_GetDefaultOutputDevice(GetProcAddress(FLibHandle, 'Pa_GetDefaultOutputDevice'));
|
||||
FPa_GetDeviceCount := TPa_GetDeviceCount(GetProcAddress(FLibHandle, 'Pa_GetDeviceCount'));
|
||||
FPa_GetDeviceInfo := TPa_GetDeviceInfo(GetProcAddress(FLibHandle, 'Pa_GetDeviceInfo'));
|
||||
FPa_OpenStream := TPa_OpenStream(GetProcAddress(FLibHandle, 'Pa_OpenStream'));
|
||||
FPa_StartStream := TPa_StartStream(GetProcAddress(FLibHandle, 'Pa_StartStream'));
|
||||
FPa_StopStream := TPa_StopStream(GetProcAddress(FLibHandle, 'Pa_StopStream'));
|
||||
@@ -275,6 +308,105 @@ begin
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Initialize — загружает PA библиотеку и вызывает Pa_Initialize без открытия стрима.
|
||||
// Нужно для перечисления устройств в настройках до старта аудио.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function TAudioOutput.Initialize: Boolean;
|
||||
var
|
||||
Err: TPaError;
|
||||
begin
|
||||
Result := False;
|
||||
if not LoadLib then Exit;
|
||||
if FPAInited then begin Result := True; Exit; end;
|
||||
Err := FPa_Initialize();
|
||||
if Err <> PA_NO_ERROR then
|
||||
begin
|
||||
FLastError := 'Pa_Initialize: ';
|
||||
if Assigned(FPa_GetErrorText) then
|
||||
FLastError := FLastError + string(FPa_GetErrorText(Err))
|
||||
else
|
||||
FLastError := FLastError + IntToStr(Err);
|
||||
Exit;
|
||||
end;
|
||||
FPAInited := True;
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// EnumOutputDevices — перечисляет устройства вывода.
|
||||
// Names получает имена устройств; Indices — соответствующие PA-индексы;
|
||||
// Count — реальное количество (может быть меньше Length(Indices)).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function TAudioOutput.EnumOutputDevices(Names: TStrings;
|
||||
out Indices: array of Integer; out Count: Integer): Boolean;
|
||||
var
|
||||
I, N: Integer;
|
||||
Info: PPaDeviceInfo;
|
||||
DevName: string;
|
||||
begin
|
||||
Count := 0;
|
||||
Result := False;
|
||||
if not Initialize then Exit;
|
||||
if not Assigned(FPa_GetDeviceCount) or not Assigned(FPa_GetDeviceInfo) then Exit;
|
||||
|
||||
N := FPa_GetDeviceCount();
|
||||
if N <= 0 then Exit;
|
||||
|
||||
for I := 0 to N - 1 do
|
||||
begin
|
||||
Info := FPa_GetDeviceInfo(I);
|
||||
if Info = nil then Continue;
|
||||
if Info^.maxOutputChannels <= 0 then Continue;
|
||||
if Count >= Length(Indices) then Break;
|
||||
|
||||
if Info^.name <> nil then
|
||||
DevName := string(AnsiString(Info^.name))
|
||||
else
|
||||
DevName := Format('Device %d', [I]);
|
||||
|
||||
Names.Add(DevName);
|
||||
Indices[Count] := I;
|
||||
Inc(Count);
|
||||
end;
|
||||
Result := Count > 0;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FindDeviceByName — возвращает PA device index по имени (-1 если не найден).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function TAudioOutput.FindDeviceByName(const AName: string): Integer;
|
||||
var
|
||||
I, N: Integer;
|
||||
Info: PPaDeviceInfo;
|
||||
DevName: string;
|
||||
begin
|
||||
Result := -1;
|
||||
if AName = '' then Exit;
|
||||
if not Initialize then Exit;
|
||||
if not Assigned(FPa_GetDeviceCount) or not Assigned(FPa_GetDeviceInfo) then Exit;
|
||||
|
||||
N := FPa_GetDeviceCount();
|
||||
for I := 0 to N - 1 do
|
||||
begin
|
||||
Info := FPa_GetDeviceInfo(I);
|
||||
if Info = nil then Continue;
|
||||
if Info^.maxOutputChannels <= 0 then Continue;
|
||||
if Info^.name <> nil then
|
||||
DevName := string(AnsiString(Info^.name))
|
||||
else
|
||||
DevName := Format('Device %d', [I]);
|
||||
if SameText(DevName, AName) then
|
||||
begin
|
||||
Result := I;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Open — точная последовательность как в audio_open_output()
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -287,28 +419,18 @@ var
|
||||
begin
|
||||
Result := False;
|
||||
if FOpen then begin Result := True; Exit; end;
|
||||
if not LoadLib then Exit;
|
||||
if not Initialize then Exit; // загрузка PA + Pa_Initialize
|
||||
|
||||
// Pa_Initialize — один раз (как в audio_get_cards)
|
||||
if not FPAInited then
|
||||
// Выбор устройства: FDeviceIndex или default
|
||||
if FDeviceIndex >= 0 then
|
||||
Dev := FDeviceIndex
|
||||
else
|
||||
begin
|
||||
Err := FPa_Initialize();
|
||||
if Err <> PA_NO_ERROR then
|
||||
begin
|
||||
FLastError := 'Pa_Initialize: ';
|
||||
if Assigned(FPa_GetErrorText) then
|
||||
FLastError := FLastError + string(FPa_GetErrorText(Err))
|
||||
else
|
||||
FLastError := FLastError + IntToStr(Err);
|
||||
Exit;
|
||||
end;
|
||||
FPAInited := True;
|
||||
Dev := FPa_GetDefaultOutputDevice();
|
||||
end;
|
||||
|
||||
Dev := FPa_GetDefaultOutputDevice();
|
||||
if Dev = PA_NO_DEV then
|
||||
begin
|
||||
FLastError := 'No default output device';
|
||||
FLastError := 'No output device available';
|
||||
Exit;
|
||||
end;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user