wip(audio): выбор устройства ВВОДА на каждом слайсе + вкладки OUT/IN в AUD

Паритет с уже существующим выбором вывода: на каждом слайсе своё устройство
входа (микрофон/виртуальный кабель).

- RadioController: TCtrlSlice += InDeviceIndex/InDevName/AudioIn (ссылка, не
  владение); mic-пул FMicPool с refcount (Acquire/ReleaseMicInput) — один
  открытый TAudioInput на уникальное PA-устройство, смена TX-слайса не
  переоткрывает поток; ActiveMicInput/ActiveMicInDevName для PullSoundCardMic,
  Flush и DefaultMicSource; SetMainMicDevice перепривязывает ссылки слайсов.
- VfoOverlay: во fly-out AUD ряд вкладок OUT | IN, событие OnAudioInDevice.
- PanafallPanel: проброс списка входных устройств во флаги слайсов и главный.
- Settings: персист in_dev_name.

НЕ ГОТОВО: зависание GUI при выборе на слайсе того же входа, что в настройках
(пул открывает поток вместо шортката на общий FAudioIn). В коде оставлена
временная диагностика [MIC] в stderr. Также подозрение на замедление потока
данных от радио — проверяем сравнением с main.

Со мной в коммите временные тестовые программы PortAudio (other/pa*.lpr).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 21:52:24 +03:00
co-authored by Claude Opus 4.8
parent 317e9b732c
commit 4aa180e7b2
9 changed files with 493 additions and 49 deletions
+25
View File
@@ -0,0 +1,25 @@
program padump;
{$MODE Delphi}
uses SysUtils, Classes, AudioInput, AudioOutput;
var
AI: TAudioInput;
AO: TAudioOutput;
Names: TStringList;
Idx: array[0..63] of Integer;
Cnt, i: Integer;
begin
AI := TAudioInput.Create(48000);
AO := TAudioOutput.Create(48000);
Names := TStringList.Create;
Cnt := 0;
AI.EnumInputDevices(Names, Idx, Cnt);
WriteLn('--- INPUT devices (', Cnt, ') ---');
for i := 0 to Cnt - 1 do WriteLn(Format('%3d %s', [Idx[i], Names[i]]));
WriteLn('FindDeviceByName(Direwolf_TX_Output) = ',
AI.FindDeviceByName('Direwolf_TX_Output'));
Names.Clear; Cnt := 0;
AO.EnumOutputDevices(Names, Idx, Cnt);
WriteLn('--- OUTPUT devices (', Cnt, ') ---');
for i := 0 to Cnt - 1 do WriteLn(Format('%3d %s', [Idx[i], Names[i]]));
Names.Free; AI.Free; AO.Free;
end.
+19
View File
@@ -0,0 +1,19 @@
program patest2;
{$MODE Delphi}
uses SysUtils, AudioInput;
var A, B: TAudioInput;
begin
A := TAudioInput.Create(48000);
A.DeviceIndex := 15; // DireWolf_TX_Output
WriteLn('open #1 ...'); Flush(Output);
WriteLn(' -> ', A.Open, ' err=', A.LastError); Flush(Output);
B := TAudioInput.Create(48000);
B.DeviceIndex := 15; // то же устройство вторым потоком
WriteLn('open #2 ...'); Flush(Output);
WriteLn(' -> ', B.Open, ' err=', B.LastError); Flush(Output);
Sleep(300);
WriteLn('avail #1=', A.Available, ' #2=', B.Available); Flush(Output);
B.Close; A.Close;
B.Free; A.Free;
WriteLn('done');
end.
+29
View File
@@ -0,0 +1,29 @@
program patest3;
{$MODE Delphi}
uses SysUtils, Classes, AudioInput;
var
E: TAudioInput;
A: TAudioInput;
Names: TStringList;
Idx: array[0..63] of Integer;
Cnt, i: Integer;
OK: Boolean;
begin
E := TAudioInput.Create(48000);
Names := TStringList.Create;
Cnt := 0;
E.EnumInputDevices(Names, Idx, Cnt);
for i := 0 to Cnt - 1 do
begin
WriteLn(Format('>>> try %3d %s', [Idx[i], Names[i]])); Flush(Output);
A := TAudioInput.Create(48000);
A.DeviceIndex := Idx[i];
OK := A.Open;
WriteLn(Format(' %3d open=%s %s', [Idx[i], BoolToStr(OK, True), A.LastError]));
Flush(Output);
A.Close;
A.Free;
end;
Names.Free; E.Free;
WriteLn('ALL DONE');
end.