Easy Setup: Use card names instead of device names for generating device list (#932)
* WIP code to get card/port names. * Fix Linux compiler errors. * Ensure that PulseAudio is adding card names when being interrogated. * Initial logic to use card names instead of device names in Easy Setup dialog. * WASAPI: Make sure Easy Setup can still work with FlexRadio virtual devices. * Remove Windows-specific logic from Easy Setup dialog code. * Add PR #932 to changelog.pull/935/head
parent
9824bdd3f9
commit
c5a041c989
|
@ -804,6 +804,7 @@ LDPC | Low Density Parity Check Codes - a family of powerful FEC codes
|
|||
* FreeDV Reporter: Fix issue with first column not being aligned properly with other columns. (PR #922)
|
||||
* FreeDV Reporter: Work around Linux bug preventing some flag emojis from being fully deleted on backspace. (PR #931)
|
||||
* Fix GTK+ assertion after FreeDV Reporter has been open for a long time. (PR #929)
|
||||
* Easy Setup: Use card names instead of device names for generating device list. (PR #932)
|
||||
2. Documentation:
|
||||
* Add missing dependency for macOS builds to README. (PR #925; thanks @relistan!)
|
||||
* Add note about using XWayland on Linux. (PR #926)
|
||||
|
|
|
@ -29,8 +29,11 @@
|
|||
struct AudioDeviceSpecification
|
||||
{
|
||||
int deviceId;
|
||||
wxString name;
|
||||
wxString apiName;
|
||||
wxString name; // Display/config name of device
|
||||
wxString cardName; // Name of the audio device
|
||||
int cardIndex; // TBD - internal data for PulseAudio to look up cardName
|
||||
wxString portName; // Name of the port from the above audio device (e.g. "Speakers" on Windows). Optional.
|
||||
wxString apiName; // Name of the active audio API
|
||||
int defaultSampleRate;
|
||||
int maxChannels;
|
||||
int minChannels;
|
||||
|
|
|
@ -363,6 +363,7 @@ AudioDeviceSpecification MacAudioEngine::getAudioSpecification_(int coreAudioId,
|
|||
AudioDeviceSpecification device;
|
||||
device.deviceId = coreAudioId;
|
||||
device.name = wxString::FromUTF8(deviceName);
|
||||
device.cardName = device.name;
|
||||
device.apiName = "Core Audio";
|
||||
device.maxChannels = numChannels;
|
||||
device.minChannels = 1;
|
||||
|
|
|
@ -135,6 +135,7 @@ std::vector<AudioDeviceSpecification> PortAudioEngine::getAudioDeviceList(AudioD
|
|||
AudioDeviceSpecification device;
|
||||
device.deviceId = index;
|
||||
device.name = wxString::FromUTF8(deviceInfo->name);
|
||||
device.cardName = device.name;
|
||||
device.apiName = hostApiName;
|
||||
|
||||
// For "whitelisted" devices, also assume channel counts
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
#include "PulseAudioDevice.h"
|
||||
#include "PulseAudioEngine.h"
|
||||
|
||||
#include <map>
|
||||
#include "../util/logging/ulog.h"
|
||||
|
||||
PulseAudioEngine::PulseAudioEngine()
|
||||
: initialized_(false)
|
||||
{
|
||||
|
@ -140,6 +143,7 @@ void PulseAudioEngine::stop()
|
|||
struct PulseAudioDeviceListTemp
|
||||
{
|
||||
std::vector<AudioDeviceSpecification> result;
|
||||
std::map<int, std::string> cardResult;
|
||||
PulseAudioEngine* thisPtr;
|
||||
};
|
||||
|
||||
|
@ -164,7 +168,8 @@ std::vector<AudioDeviceSpecification> PulseAudioEngine::getAudioDeviceList(Audio
|
|||
|
||||
AudioDeviceSpecification device;
|
||||
device.deviceId = i->index;
|
||||
device.name = i->name;
|
||||
device.name = wxString::FromUTF8(i->name);
|
||||
device.cardIndex = i->card;
|
||||
device.apiName = "PulseAudio";
|
||||
device.maxChannels = i->sample_spec.channels;
|
||||
device.minChannels = 1; // TBD: can minimum be >1 on PulseAudio or pipewire?
|
||||
|
@ -187,7 +192,8 @@ std::vector<AudioDeviceSpecification> PulseAudioEngine::getAudioDeviceList(Audio
|
|||
|
||||
AudioDeviceSpecification device;
|
||||
device.deviceId = i->index;
|
||||
device.name = i->name;
|
||||
device.name = wxString::FromUTF8(i->name);
|
||||
device.cardIndex = i->card;
|
||||
device.apiName = "PulseAudio";
|
||||
device.maxChannels = i->sample_spec.channels;
|
||||
device.minChannels = 1; // TBD: can minimum be >1 on PulseAudio or pipewire?
|
||||
|
@ -204,8 +210,40 @@ std::vector<AudioDeviceSpecification> PulseAudioEngine::getAudioDeviceList(Audio
|
|||
pa_threaded_mainloop_wait(mainloop_);
|
||||
}
|
||||
|
||||
pa_operation_unref(op);
|
||||
pa_operation_unref(op);
|
||||
|
||||
// Get list of cards
|
||||
op = pa_context_get_card_info_list(context_, [](pa_context *c, const pa_card_info *i, int eol, void *userdata)
|
||||
{
|
||||
PulseAudioDeviceListTemp* tempObj = static_cast<PulseAudioDeviceListTemp*>(userdata);
|
||||
|
||||
if (eol)
|
||||
{
|
||||
pa_threaded_mainloop_signal(tempObj->thisPtr->mainloop_, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
tempObj->cardResult[i->index] = i->name;
|
||||
}, &tempObj);
|
||||
|
||||
// Wait for the operation to complete
|
||||
for(;;)
|
||||
{
|
||||
if (pa_operation_get_state(op) != PA_OPERATION_RUNNING) break;
|
||||
pa_threaded_mainloop_wait(mainloop_);
|
||||
}
|
||||
|
||||
pa_operation_unref(op);
|
||||
pa_threaded_mainloop_unlock(mainloop_);
|
||||
|
||||
// Iterate over result and populate cardName
|
||||
for (auto& obj : tempObj.result)
|
||||
{
|
||||
if (tempObj.cardResult.find(obj.cardIndex) != tempObj.cardResult.end())
|
||||
{
|
||||
obj.cardName = wxString::FromUTF8(tempObj.cardResult[obj.cardIndex].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return tempObj.result;
|
||||
}
|
||||
|
|
|
@ -179,6 +179,7 @@ std::vector<AudioDeviceSpecification> WASAPIAudioEngine::getAudioDeviceList(Audi
|
|||
{
|
||||
devSpec.deviceId = index;
|
||||
result.push_back(devSpec);
|
||||
log_debug("Found device %s (card = %s, port = %s)", (const char*)devSpec.name.ToUTF8(), (const char*)devSpec.cardName.ToUTF8(), (const char*)devSpec.portName.ToUTF8());
|
||||
}
|
||||
device->Release();
|
||||
}
|
||||
|
@ -381,6 +382,73 @@ AudioDeviceSpecification WASAPIAudioEngine::getDeviceSpecification_(IMMDevice* d
|
|||
AudioDeviceSpecification spec;
|
||||
spec.name = wxString::FromUTF8(getUTF8String_(friendlyName.pwszVal));
|
||||
spec.apiName = "Windows WASAPI";
|
||||
|
||||
// Get card and port info
|
||||
hr = propStore->GetValue(PKEY_DeviceInterface_FriendlyName, &friendlyName);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Could not get card name (hr = " << hr << ")";
|
||||
log_error(ss.str().c_str());
|
||||
if (onAudioErrorFunction)
|
||||
{
|
||||
onAudioErrorFunction(*this, ss.str(), onAudioErrorState);
|
||||
}
|
||||
PropVariantClear(&friendlyName);
|
||||
propStore->Release();
|
||||
return AudioDeviceSpecification::GetInvalidDevice();
|
||||
}
|
||||
|
||||
if (friendlyName.vt == VT_EMPTY)
|
||||
{
|
||||
log_warn("Device does not have a card name!");
|
||||
PropVariantClear(&friendlyName);
|
||||
propStore->Release();
|
||||
return AudioDeviceSpecification::GetInvalidDevice();
|
||||
}
|
||||
|
||||
spec.cardName = wxString::FromUTF8(getUTF8String_(friendlyName.pwszVal));
|
||||
|
||||
hr = propStore->GetValue(PKEY_Device_DeviceDesc, &friendlyName);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Could not get port name (hr = " << hr << ")";
|
||||
log_error(ss.str().c_str());
|
||||
if (onAudioErrorFunction)
|
||||
{
|
||||
onAudioErrorFunction(*this, ss.str(), onAudioErrorState);
|
||||
}
|
||||
PropVariantClear(&friendlyName);
|
||||
propStore->Release();
|
||||
return AudioDeviceSpecification::GetInvalidDevice();
|
||||
}
|
||||
|
||||
if (friendlyName.vt == VT_EMPTY)
|
||||
{
|
||||
log_warn("Device does not have a port name!");
|
||||
PropVariantClear(&friendlyName);
|
||||
propStore->Release();
|
||||
return AudioDeviceSpecification::GetInvalidDevice();
|
||||
}
|
||||
|
||||
spec.portName = wxString::FromUTF8(getUTF8String_(friendlyName.pwszVal));
|
||||
bool noPortName = spec.portName.Length() == 0 && spec.cardName != spec.name;
|
||||
bool isFlexRadio = spec.cardName.StartsWith("FlexRadio");
|
||||
|
||||
if (noPortName)
|
||||
{
|
||||
// If there's no port name, just use the same name for cardName
|
||||
// as the card's full name.
|
||||
spec.cardName = spec.name;
|
||||
}
|
||||
else if (isFlexRadio)
|
||||
{
|
||||
// We also have a carveout for FlexRadio virtual audio devices
|
||||
// so that Easy Setup can automatically group the RX and TX
|
||||
// devices together.
|
||||
spec.cardName = spec.portName;
|
||||
}
|
||||
|
||||
// Activate IAudioClient so we can obtain format info
|
||||
IAudioClient* audioClient = nullptr;
|
||||
|
|
|
@ -1151,14 +1151,14 @@ void EasySetupDialog::updateAudioDevices_()
|
|||
|
||||
for (auto& dev : inputDevices)
|
||||
{
|
||||
if (dev.name.Find(_("Microsoft Sound Mapper")) != -1 ||
|
||||
dev.name.Find(_(" [Loopback]")) != -1)
|
||||
if (dev.cardName.Find(_("Microsoft Sound Mapper")) != -1 ||
|
||||
dev.cardName.Find(_(" [Loopback]")) != -1)
|
||||
{
|
||||
// Sound mapper and loopback devices should be skipped.
|
||||
continue;
|
||||
}
|
||||
|
||||
wxString cleanedDeviceName = dev.name;
|
||||
wxString cleanedDeviceName = dev.cardName;
|
||||
|
||||
SoundDeviceData* soundData = new SoundDeviceData();
|
||||
assert(soundData != nullptr);
|
||||
|
@ -1187,74 +1187,14 @@ void EasySetupDialog::updateAudioDevices_()
|
|||
|
||||
for (auto& dev : outputDevices)
|
||||
{
|
||||
if (dev.name.Find(_("Microsoft Sound Mapper")) != -1 ||
|
||||
dev.name.Find(_(" [Loopback]")) != -1)
|
||||
if (dev.cardName.Find(_("Microsoft Sound Mapper")) != -1 ||
|
||||
dev.cardName.Find(_(" [Loopback]")) != -1)
|
||||
{
|
||||
// Sound mapper and loopback devices should be skipped.
|
||||
continue;
|
||||
}
|
||||
|
||||
// For Windows, some devices have a designator at the beginning
|
||||
// (e.g. "Microphone (USB Audio CODEC)" and "Speakers (USB Audio CODEC)".
|
||||
// We need to be able to strip the designator without affecting
|
||||
// the actual name of the device. To do this, we remove a character
|
||||
// at a time from the beginning of the device name until we find a
|
||||
// device in the current list with the same suffix. If we do find
|
||||
// such a device, then we use this suffix as the device name to show
|
||||
// in the list instead.
|
||||
wxString cleanedDeviceName = dev.name;
|
||||
if (finalRadioDeviceList.find(dev.name) == finalRadioDeviceList.end())
|
||||
{
|
||||
SoundDeviceData* foundItem = nullptr;
|
||||
wxString oldDeviceName;
|
||||
do
|
||||
{
|
||||
for (auto& kvp : finalRadioDeviceList)
|
||||
{
|
||||
if (kvp.first.Find("DAX") == 0)
|
||||
{
|
||||
// FlexRadio devices are treated differently
|
||||
// so we shouldn't consider them here.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto suffix = kvp.first.Mid(kvp.first.size() - cleanedDeviceName.size());
|
||||
if (suffix == cleanedDeviceName)
|
||||
{
|
||||
foundItem = kvp.second;
|
||||
oldDeviceName = kvp.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundItem == nullptr)
|
||||
{
|
||||
cleanedDeviceName = cleanedDeviceName.Mid(1);
|
||||
}
|
||||
} while (cleanedDeviceName.Length() > 5 && foundItem == nullptr);
|
||||
|
||||
if (foundItem == nullptr)
|
||||
{
|
||||
cleanedDeviceName = dev.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rename device in device list to "cleaned up" name.
|
||||
cleanedDeviceName.Trim(false);
|
||||
cleanedDeviceName.Trim(true);
|
||||
auto parenthesisLoc = cleanedDeviceName.Find(_("("));
|
||||
if (parenthesisLoc >= 0)
|
||||
{
|
||||
cleanedDeviceName = cleanedDeviceName.Mid(parenthesisLoc + 1);
|
||||
if (cleanedDeviceName.Right(1) == _(")"))
|
||||
{
|
||||
cleanedDeviceName.RemoveLast(1);
|
||||
}
|
||||
}
|
||||
|
||||
finalRadioDeviceList.erase(oldDeviceName);
|
||||
finalRadioDeviceList[cleanedDeviceName] = foundItem;
|
||||
}
|
||||
}
|
||||
wxString cleanedDeviceName = dev.cardName;
|
||||
|
||||
SoundDeviceData* soundData = finalRadioDeviceList[cleanedDeviceName];
|
||||
if (soundData == nullptr)
|
||||
|
|
Loading…
Reference in New Issue