Recent

Author Topic: [SOLVED] List audio devices (cross platform)  (Read 5713 times)

tudi_x

  • Hero Member
  • *****
  • Posts: 532
[SOLVED] List audio devices (cross platform)
« on: August 08, 2017, 09:22:30 am »
hi All,
cross platform (Lazarus 1.8RC3) i would need to enumerate the list of audio devices the application user would be having access to.
i see in http://wiki.freepascal.org/Audio_libraries there are a lot of audio libraries.
please advise if there would be a way to get the list of audio devices without these libraries as they look a little bit laborious for what i need. especially on Windows as on Unix i understood i could parse some command outputs.

thank you

« Last Edit: August 08, 2017, 04:39:37 pm by tudi_x »
Lazarus 2.0.2 64b on Debian LXDE 10

tudi_x

  • Hero Member
  • *****
  • Posts: 532
Re: List audio devices (cross platform)
« Reply #1 on: August 08, 2017, 03:46:07 pm »
for Windows the WMI information looks like not providing the exact model of the device.
please advise if i could be using JwaWindows in order to access Windows's DirectSound in order to enumerate the sound devices (microphones or speakers).

how could i do this call? i did not find documentation on accessing directly DirectSound with Lazarus.

thank you
Lazarus 2.0.2 64b on Debian LXDE 10

Fred vS

  • Hero Member
  • *****
  • Posts: 3835
    • StrumPract is the musicians best friend
Re: List audio devices (cross platform)
« Reply #2 on: August 08, 2017, 03:51:47 pm »
Hello.

You may use uos : https://github.com/fredvs/uos

Take a look at /uos/examples/deviceinfos.lpi

Fre;D
« Last Edit: August 08, 2017, 04:06:11 pm by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

tudi_x

  • Hero Member
  • *****
  • Posts: 532
Re: List audio devices (cross platform)
« Reply #3 on: August 08, 2017, 04:39:17 pm »
thank you very much!
in deviceinfos.lpi i have the information i need.
Lazarus 2.0.2 64b on Debian LXDE 10

ASerge

  • Hero Member
  • *****
  • Posts: 2477
Re: List audio devices (cross platform)
« Reply #4 on: August 08, 2017, 09:15:53 pm »
for Windows the WMI information looks like not providing the exact model of the device.
please advise if i could be using JwaWindows in order to access Windows's DirectSound in order to enumerate the sound devices (microphones or speakers).
how could i do this call? i did not find documentation on accessing directly DirectSound with Lazarus.
Example.
In an empty project, add the definition to the implementation part:
Code: Pascal  [Select][+][-]
  1. uses Windows, ActiveX, ComObj;
  2.  
  3. // Start API extracted from MSDN
  4.  
  5. const
  6.   CLASS_IMMDeviceEnumerator: TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
  7.  
  8. const
  9.   DEVICE_STATE_ACTIVE     = $00000001;
  10.   DEVICE_STATE_DISABLED   = $00000002;
  11. //  DEVICE_STATE_NOTPRESENT = $00000004;
  12.   DEVICE_STATE_UNPLUGGED  = $00000008;
  13. //  DEVICE_STATEMASK_ALL    = $0000000F;
  14.  
  15. {$MINENUMSIZE 4}
  16. type
  17.   EDataFlow = (
  18.     eRender,
  19.     eCapture,
  20.     eAll,
  21.     EDataFlow_enum_count);
  22.  
  23.   ERole = (
  24.     eConsole,
  25.     eMultimedia,
  26.     eCommunications,
  27.     ERole_enum_count);
  28.  
  29.   TPropertyKey = record
  30.     fmtid: TGUID;
  31.     pid: DWORD;
  32.   end;
  33.  
  34.   IMMNotificationClient = interface(IUnknown)
  35.     ['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
  36.     function OnDeviceStateChanged(pwstrDeviceId: LPWSTR;
  37.       dwNewState: DWORD): HRESULT; stdcall;
  38.     function OnDeviceAdded(pwstrDeviceId: LPWSTR): HRESULT; stdcall;
  39.     function OnDeviceRemoved(pwstrDeviceId: LPWSTR): HRESULT; stdcall;
  40.     function OnDefaultDeviceChanged(flow: EDataFlow; role: ERole;
  41.       pwstrDefaultDeviceId: LPWSTR): HRESULT; stdcall;
  42.     function OnPropertyValueChanged(pwstrDeviceId: LPWSTR;
  43.       const key: TPropertyKey): HRESULT; stdcall;
  44.   end;
  45.  
  46.   IPropertyStore = interface(IUnknown)
  47.     function GetCount(out cProps: DWORD): HRESULT; stdcall;
  48.     function GetAt(iProp: DWORD; out key: TPropertyKey): HRESULT; stdcall;
  49.     function GetValue(const key: TPropertyKey;
  50.       out value: TPropVariant): HRESULT; stdcall;
  51.   end;
  52.  
  53.   IMMDevice = interface(IUnknown)
  54.     ['{D666063F-1587-4E43-81F1-B948E807363F}']
  55.     function Activate(const iid: TGUID; dwClsCtx: DWORD;
  56.       pActivationParams: PPropVariant;
  57.       out EndpointVolume: IUnknown): HRESULT; stdcall;
  58.     function OpenPropertyStore(stgmAccess: DWORD;
  59.       out Properties: IPropertyStore): HRESULT; stdcall;
  60.     function GetId(out strId: LPWSTR): HRESULT; stdcall;
  61.     function GetState(out State: DWORD): HRESULT; stdcall;
  62.   end;
  63.  
  64.   IMMDeviceCollection = interface(IUnknown)
  65.     ['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
  66.     function GetCount(out cDevices: UINT): HRESULT; stdcall;
  67.     function Item(nDevice: UINT; out Device: IMMDevice): HRESULT; stdcall;
  68.   end;
  69.  
  70.   IMMDeviceEnumerator = interface(IUnknown)
  71.     ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
  72.     function EnumAudioEndpoints(dataFlow: EDataFlow;
  73.       dwStateMask: DWORD; out Devices: IMMDeviceCollection): HRESULT; stdcall;
  74.     function GetDefaultAudioEndpoint(EDF: EDataFlow; role: ERole;
  75.       out EndPoint: IMMDevice): HRESULT; stdcall;
  76.     function GetDevice(pwstrId: LPWSTR; out EndPoint: IMMDevice): HRESULT; stdcall;
  77.     function RegisterEndpointNotificationCallback(
  78.       const Client: IMMNotificationClient): HRESULT; stdcall;
  79.     function UnregisterEndpointNotificationCallback(
  80.       const Client: IMMNotificationClient): HRESULT; stdcall;
  81.   end;
  82.  
  83.   // End API
  84.  
  85.   // Own wrapper
  86.  
  87.   TDeviceState = (dsUnknown, dsActive, dsDisabled, dsUnplagged);
  88.  
  89.   TDevice = class(TObject)
  90.   private
  91.     FId: UnicodeString;
  92.     FName: UnicodeString;
  93.     FState: TDeviceState;
  94.     FDefault: Boolean;
  95.   public
  96.     constructor Create(const MMDevice: IMMDevice);
  97.     property Id: UnicodeString read FId;
  98.     property Name: UnicodeString read FName;
  99.     property State: TDeviceState read FState;
  100.     property Default: Boolean read FDefault;
  101.   end;
  102.  
  103.   TDeviceList = class(TObject)
  104.   private
  105.     FList: array of TDevice;
  106.     function GetCount: Integer;
  107.     function GetItem(Index: Integer): TDevice;
  108.   public
  109.     constructor Create;
  110.     destructor Destroy; override;
  111.     property Count: Integer read GetCount;
  112.     property Items[Index: Integer]: TDevice read GetItem; default;
  113.   end;
  114.  
  115. constructor TDeviceList.Create;
  116. const
  117.   CValidState = DEVICE_STATE_ACTIVE or
  118.     DEVICE_STATE_DISABLED or
  119.     DEVICE_STATE_UNPLUGGED;
  120. var
  121.   DE: IMMDeviceEnumerator;
  122.   DC: IMMDeviceCollection;
  123.   i, Cnt: UINT;
  124.   MMDevice: IMMDevice;
  125.   Device: TDevice;
  126. begin
  127.   inherited Create;
  128.   DE := CreateComObject(CLASS_IMMDeviceEnumerator) as IMMDeviceEnumerator;
  129.   OleCheck(DE.EnumAudioEndpoints(eRender, CValidState, DC));
  130.   OleCheck(DC.GetCount(Cnt));
  131.   SetLength(FList, Cnt);
  132.   for i := 0 to Cnt - 1 do
  133.   begin
  134.     OleCheck(DC.Item(i, MMDevice));
  135.     FList[i] := TDevice.Create(MMDevice);
  136.   end;
  137.   if DE.GetDefaultAudioEndpoint(eRender, eConsole, MMDevice) = S_OK then
  138.   begin
  139.     Device := TDevice.Create(MMDevice);
  140.     try
  141.       for i := Low(FList) to High(FList) do
  142.         if FList[i].Id = Device.Id then
  143.         begin
  144.           FList[i].FDefault := True;
  145.           Break;
  146.         end;
  147.     finally
  148.       Device.Free;
  149.     end;
  150.   end;
  151. end;
  152.  
  153. destructor TDeviceList.Destroy;
  154. var
  155.   i: Integer;
  156. begin
  157.   for i := Low(FList) to High(FList) do
  158.     FList[i].Free;
  159.   inherited;
  160. end;
  161.  
  162. function TDeviceList.GetCount: Integer;
  163. begin
  164.   Result := Length(FList);
  165. end;
  166.  
  167. function TDeviceList.GetItem(Index: Integer): TDevice;
  168. begin
  169.   Result := FList[Index];
  170. end;
  171.  
  172. function PropVariantClear(var PropVar: TPropVariant): HRESULT; stdcall;
  173.   external 'ole32.dll';
  174.  
  175. constructor TDevice.Create(const MMDevice: IMMDevice);
  176.  
  177.   procedure PropVariantInit(out PropVar: TPropVariant); inline;
  178.   begin
  179.     ZeroMemory(@PropVar, SizeOf(PropVar));
  180.   end;
  181.  
  182. const
  183.   PKEY_Device_FriendlyName: TPropertyKey =
  184.     (fmtid:'{A45C254E-DF1C-4EFD-8020-67D146A850E0}'; pid:14); // DEVPROP_TYPE_STRING
  185. var
  186.   Props: IPropertyStore;
  187.   VarName: TPropVariant;
  188.   DevState: DWORD;
  189.   AId: PWideChar;
  190. begin
  191.   inherited Create;
  192.   OleCheck(MMDevice.GetState(DevState));
  193.   OleCheck(MMDevice.OpenPropertyStore(STGM_READ, Props));
  194.   OleCheck(MMDevice.GetId(AId));
  195.   FId := AId;
  196.   CoTaskMemFree(AId);
  197.   PropVariantInit(VarName);
  198.   OleCheck(Props.GetValue(PKEY_Device_FriendlyName, VarName));
  199.   FName := VarName.pwszVal;
  200.   case DevState of
  201.     DEVICE_STATE_ACTIVE:
  202.       FState := dsActive;
  203.     DEVICE_STATE_DISABLED:
  204.       FState := dsDisabled;
  205.     DEVICE_STATE_UNPLUGGED:
  206.       FState := dsUnplagged;
  207.   else
  208.     FState := dsUnknown;
  209.   end;
  210.   PropVariantClear(VarName);
  211.   Props := nil;
  212. end;

and then code to use it:
Code: Pascal  [Select][+][-]
  1. // above uses ...ComCtrls
  2. procedure TForm1.FormCreate(Sender: TObject);
  3.  
  4.   procedure InitListView(ListView: TListView) inline;
  5.   begin
  6.     ListView.Align := alClient;
  7.     ListView.ViewStyle := vsReport;
  8.     ListView.Checkboxes := True;
  9.     ListView.AutoWidthLastColumn := True;
  10.   end;
  11.  
  12.   procedure InitColumns(ListView: TListView) inline;
  13.   var
  14.     Col: TListColumn;
  15.   begin
  16.     Col := ListView.Columns.Add;
  17.     Col.Caption := 'Name';
  18.     Col.Width := 300;
  19.     Col := ListView.Columns.Add;
  20.     Col.Caption := 'State';
  21.     Col.Width := 70;
  22.     ListView.Columns.Add.Caption := 'Id';
  23.   end;
  24.  
  25.   procedure AddListItem(ToList: TListView; Device: TDevice); inline;
  26.   const
  27.     CStateName: array[TDeviceState] of string = (
  28.       'Unknown', 'Active', 'Disabled', 'Unplagged');
  29.   var
  30.     Item: TListItem;
  31.   begin
  32.     Item := ToList.Items.Add;
  33.     Item.Caption := UTF8Encode(Device.Name);
  34.     Item.SubItems.Append(CStateName[Device.State]);
  35.     Item.SubItems.Append(UTF8Encode(Device.Id));
  36.     Item.Checked := Device.Default;
  37.   end;
  38.  
  39. var
  40.   L: TDeviceList;
  41.   i: Integer;
  42.   ListView: TListView;
  43. begin
  44.   ListView := TListView.Create(Self);
  45.   ListView.Parent := Self;
  46.   InitListView(ListView);
  47.   InitColumns(ListView);
  48.   L := TDeviceList.Create;
  49.   try
  50.     for i := 0 to L.Count - 1 do
  51.       AddListItem(ListView, L[i]);
  52.   finally
  53.     L.Free;
  54.   end;
  55. end;

 

TinyPortal © 2005-2018