Recent

Author Topic: Reliability of Device ID provided by PortAudio during the run-time of an app.  (Read 1069 times)

Nevada Smith

  • New Member
  • *
  • Posts: 14
I am using PortAudio + SndFile combination in a cross platform desktop application.

I want to provide the option to the users to select the output device in Tools->Options, and store it in the application configuration across application sessions. I started by storing the device ID (based on the index from Pa_GetDeviceCount), but found later on that the number of available devices can change, even at run-time. For example, once I started an audio player (in parallel) to listen to some music in Lubuntu, and the number of available devices reduced.
So I started storing the Device Name in the configuration file instead of the device ID.  Then at run-time, I loop through the devices and find the ID of the device.

But even then, if the available devices can change at run-time, it is possible that this can happen between the two steps. That is, after we decide the device ID and before Pa_Openstream. If this happens, audio will be played in a different device than what we intended to. This does not seem right, and can lead to hard to debug runtime inconsistent behaviour. Is there a better way to do this?

metis

  • Full Member
  • ***
  • Posts: 168
Strange, this never happens to me on Windows, nor on Linux with Wine.
Which Wrapper do You use ? Put Your Pa-Code into Your Thread to have it checked.
(Show me Yours, I'll show You mine)  :)
Life could be so easy, if there weren't those f*** Details.

Nevada Smith

  • New Member
  • *
  • Posts: 14
I am not using a wrapper (if I understood the question correctly), but rather calling the Pa_ calls directly from the dynamic library.
Code: Pascal  [Select][+][-]
  1.   TAudio.FDeviceNames.Clear;
  2.   NumDevices := Pa_GetDeviceCount();
  3.   if NumDevices < 0 then
  4.   begin
  5.     DebugLn('Pa_GetDeviceCount failed ');
  6.     DebugLn('Error after Pa_GetDeviceCount ' + IntToStr(NumDevices));
  7.   end;
  8.  
  9.   for Count := 0 to NumDevices - 1 do
  10.   begin
  11.     DeviceInfo := Pa_GetDeviceInfo(Count);
  12.     if DeviceInfo = nil then
  13.     begin
  14.       DebugLn('Error after GetDeviceInfo for device #' + IntToStr(Count));
  15.       //FDeviceNames.Clear;
  16.       FDeviceNames.Add('Unknown');
  17.     end
  18.     else
  19.     begin
  20.       DeviceName := StrPas(DeviceInfo^.Name);
  21.       FDeviceNames.Add(DeviceName);
  22.     end;
  23.  
  24.   end;  
  25.  

MarkMLl

  • Hero Member
  • *****
  • Posts: 873
You say that you see the device count change, but are you also seeing named device identifiers change (i.e. a name as distinct from a position in a list)?

I don't use PortAudio but I've certainly seen the population of other types of device change. From experience with the ALSA API I remember a list of soundcards etc. which could get confusing if there was more than one card of a given type.

However the major question is whether you can convert a name to a bus position etc. or to any kind of serialisation or hardware device ID by (for example) looking at /dev/snd/by-path and then finding a corresponding description in the /sys filesystem.

MarkMLl
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

Nevada Smith

  • New Member
  • *
  • Posts: 14
Only the device count changes. The names of the devices are consistent. And I am fine with storing the device name. But that still leaves one eventuality, that can spring a surprise.
Step 1: I take the stored device name. Enumerate devices and get the Id matching to the device name.
Step 2: I call Pa_OpenStream with device Id.
What if the list of devices change between Step 1 and Step 2?

I went through the Portaudio documentation again and there does not seem to be a straightforward answer to this.

MarkMLl

  • Hero Member
  • *****
  • Posts: 873
Only the device count changes. The names of the devices are consistent. And I am fine with storing the device name. But that still leaves one eventuality, that can spring a surprise.
Step 1: I take the stored device name. Enumerate devices and get the Id matching to the device name.
Step 2: I call Pa_OpenStream with device Id.
What if the list of devices change between Step 1 and Step 2?

The real problem there is if a device is removed or added at the start of the list, and the (numeric?) ID changes for later devices.

I went through the Portaudio documentation again and there does not seem to be a straightforward answer to this.

If the API has no provision for opening by name (or alternatively an indivisible enumerate-and-open operation) that's something that you'll have to take up with that project's maintainers.

I anticipate revisiting ALSA over the next few days and will keep my eyes open for that problem, just in case it applies to more than PortAudio... I really can't remember how I handled this last time which was several years ago.

MarkMLl
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

metis

  • Full Member
  • ***
  • Posts: 168
@Nevada Smith

Your CodeSample does not show, how You link to Portaudio, nor how You declare the Variables for it.
Quote
calling the Pa_ calls directly from the dynamic library
Using a Pa-Wrapper, like the one elaborated by Fred vS in his 'UOS' (see attached 'uos_portaudio.pas.txt') is much easier.
It works for me on several Platforms (WinXP, Win7, Win10, Linux Mint with Wine - all 32bit) w/o any Problems.  ;)

And here some possible Reasons for Problems when Opening a SoundDevice with Portaudio:

1. 'Pa_GetDeviceCount()' returns the Number of all available SoundDevices.
It does not differ between In- and OutputDevices, nor between Host-APIs - You have to do it in Your Code and You don't.
As an Example, see attached 'ListOfAvailablePaOutputDevices.jpg':
The Terminal on the left shows the List of available Pa-OutputDevices, that is generated, when my 'FFPlay4Laz' opens a MediaFile with Portaudio on Linux Mint with Wine. There You can see, that:
- the OutputDevice-Indices are not consecutive
- 'Pulseaudio' appears 3 Times with 3 different Indices for 3 different Host-APIs:
   3=MME, 7=Windows DirectSound and 9=Windows WASAPI.

2. If a Device appears in that List does not mean for sure, that it will work on Your System. You simply have to try it out.

3. Depending on Your Soundcard, Portaudio won't be able to play several Tracks simultaneously on Your System.

4. If You do not call 'Pa_Terminate()' before exiting Your App, an AudioDevice may not be available until the next Reboot, so that the Number of Pa-SoundDevices changes.  :)
Life could be so easy, if there weren't those f*** Details.

metis

  • Full Member
  • ***
  • Posts: 168
@Nevada Smith

This is basically the Code I use to enumerate SoundDevices with Portaudio
together with the UOS-Pa-Wrapper, that I've attached to my previous Post:

Code: [Select]
uses
  ..., uos_portaudio, ...;

...

procedure Pa_EnumDevices();
var
  n: LongInt;
  nPaDevInfo       : PPaDeviceInfo;
  nPaDevHostAPIInfo: PPaHostApiInfo;
begin

  for n := 0 to Pred(Pa_GetDeviceCount()) do
  begin
    nPaDevInfo := Pa_GetDeviceInfo(n);

//  if (nPaDevInfo^.maxInputChannels  > 0) then {<- to enum Pa-INputs  }
    if (nPaDevInfo^.maxOutputChannels > 0) then {<- to enum Pa-OUTputs }
    begin
      nPaDevHostAPIInfo := Pa_GetHostApiInfo(nPaDevInfo^.HostApi);
      if (nPaDevHostAPIInfo <> nil) then
        WriteLn( Format('#%2d = %s: %s', [n, nPaDevHostAPIInfo^._name, nPaDevInfo^._name]) );
    end;
  end;

end;

Hope, this solves Your Problem.  :)
« Last Edit: January 05, 2020, 07:10:43 pm by metis »
Life could be so easy, if there weren't those f*** Details.

 

TinyPortal © 2005-2018