|
From: Lasse Kärkkäi. <tr...@us...> - 2012-11-13 01:27:06
|
Author: Lasse Karkkainen <tro...@tr...>
Date: Tue Nov 13 02:28:24 2012 +0200
Change portaudio::AudioDevices API so that device ID is contained within DeviceInfo. The index within devices[] no longer has any meaning.
Omit certain useless devices listed by PortAudio.
Major cleanup of audio device detection logic in audio.cc.
NEEDS TESTING!!!
---
game/audio.cc | 103 ++++++++++++++++++------------------------
game/libda/portaudio.hpp | 14 +++++-
game/screen_audiodevices.cc | 4 +-
3 files changed, 56 insertions(+), 65 deletions(-)
diff --git a/game/audio.cc b/game/audio.cc
index 224d024..43262c4 100644
--- a/game/audio.cc
+++ b/game/audio.cc
@@ -456,69 +456,54 @@ struct Audio::Impl {
int tmp;
if (iss >> tmp && iss.get() == EOF && tmp >= 0 && tmp < count) dev = tmp;
}
+ portaudio::AudioDevices ad;
+ // Try name search with full match
+ for (unsigned i = 0; i < ad.devices.size() && dev < 0; ++i) {
+ portaudio::DeviceInfo& info = ad.devices[i];
+ if (info.name == params.dev) dev = info.idx;
+ }
+ // Try name search with partial match
+ for (unsigned i = 0; i < ad.devices.size() && dev < 0; ++i) {
+ portaudio::DeviceInfo& info = ad.devices[i];
+ if (info.name.find(params.dev) != std::string::npos) dev = info.idx;
+ }
+ if (dev < 0) throw std::runtime_error("No such device.");
std::clog << "audio/info: Trying audio device \"" << params.dev << "\", id: " << dev
<< ", in: " << params.in << ", out: " << params.out << std::endl;
- bool skip_partial = false;
- bool found = false;
- portaudio::AudioDevices ad;
- // Try exact match first, then partial
- for (int match_partial = 0; match_partial < 2 && !skip_partial; ++match_partial) {
- // Loop through the devices and try everything that matches the name
- for (int i = -1; i < count && (dev < 0 || i == -1); ++i) {
- if (dev >= 0 && i == -1) i = dev;
- else if (i == -1) continue;
- portaudio::DeviceInfo& info = ad.devices[i];
- if (info.name.empty()) continue;
- if (info.in < int(params.mics.size())) continue;
- if (info.out < params.out) continue;
- if (dev < 0) { // Try matching by name
- if (!match_partial && info.name != params.dev) continue;
- if (match_partial && info.name.find(params.dev) == std::string::npos) continue;
- }
- // Match found if we got here
- int assigned_mics = 0;
- try {
- Device* d = new Device(params.in, params.out, params.rate, i);
- devices.push_back(d);
- // Start capture/playback on this device (likely to throw due to audio system errors)
- // NOTE: When it throws we want to keep the device in devices to avoid calling ~Device
- // which often would hit the Pa_CloseStream hang bug and terminate the application.
- d->start();
- // Assign mics for all channels of the device
- for (unsigned int j = 0; j < params.in; ++j) {
- if (analyzers.size() >= 4) break; // Too many mics
- std::string const& m = params.mics[j];
- if (m.empty()) continue; // Input channel not used
- // Check that the color is not already taken
- bool mic_used = false;
- for (size_t mi = 0; mi < analyzers.size(); ++mi) {
- if (analyzers[mi].getId() == m) { mic_used = true; break; }
- }
- if (mic_used) continue;
- // Add the new analyzer
- Analyzer* a = new Analyzer(d->rate, m);
- analyzers.push_back(a);
- d->mics[j] = a;
- ++assigned_mics;
- }
- // Assign playback output for the first available stereo output
- if (!playback && d->out == 2) { d->outptr = &output; playback = true; }
- } catch (std::runtime_error& e) {
- std::clog << "audio/warning: " << info.name << ": " << e.what() << std::endl;
- if (dev > 0) { skip_partial = true; break; } // Numeric, end search
- continue;
- }
- skip_partial = true;
- found = true;
- std::clog << "audio/info: Using audio device: " << i;
- if (assigned_mics) std::clog << ", input channels: " << assigned_mics;
- if (params.out) std::clog << ", output channels: " << params.out;
- std::clog << std::endl;
- break;
+ portaudio::DeviceInfo& info = ad.devices[dev];
+ if (info.in < int(params.mics.size())) throw std::runtime_error("Device doesn't have enough input channels");
+ if (info.out < params.out) throw std::runtime_error("Device doesn't have enough output channels");
+ // Match found if we got here, construct a device
+ Device* d = new Device(params.in, params.out, params.rate, info.idx);
+ devices.push_back(d);
+ // Start capture/playback on this device (likely to throw due to audio system errors)
+ // NOTE: When it throws we want to keep the device in devices to avoid calling ~Device
+ // which often would hit the Pa_CloseStream hang bug and terminate the application.
+ d->start();
+ // Assign mics for all channels of the device
+ int assigned_mics = 0;
+ for (unsigned int j = 0; j < params.in; ++j) {
+ if (analyzers.size() >= 4) break; // Too many mics
+ std::string const& m = params.mics[j];
+ if (m.empty()) continue; // Input channel not used
+ // Check that the color is not already taken
+ bool mic_used = false;
+ for (size_t mi = 0; mi < analyzers.size(); ++mi) {
+ if (analyzers[mi].getId() == m) { mic_used = true; break; }
}
+ if (mic_used) continue;
+ // Add the new analyzer
+ Analyzer* a = new Analyzer(d->rate, m);
+ analyzers.push_back(a);
+ d->mics[j] = a;
+ ++assigned_mics;
}
- // Error handling
- if (!found) throw std::runtime_error("Not found or already in use.");
+ // Assign playback output for the first available stereo output
+ if (!playback && d->out == 2) { d->outptr = &output; playback = true; }
+ std::clog << "audio/info: Using audio device: " << dev;
+ if (assigned_mics) std::clog << ", input channels: " << assigned_mics;
+ if (params.out) std::clog << ", output channels: " << params.out;
+ std::clog << std::endl;
} catch(std::runtime_error& e) {
std::clog << "audio/error: Audio device '" << *it << "': " << e.what() << std::endl;
}
diff --git a/game/libda/portaudio.hpp b/game/libda/portaudio.hpp
index 99d54b3..2e4c091 100644
--- a/game/libda/portaudio.hpp
+++ b/game/libda/portaudio.hpp
@@ -34,7 +34,7 @@ namespace portaudio {
}
struct DeviceInfo {
- DeviceInfo(std::string n = "", int i = 0, int o = 0): name(n), in(i), out(o) {}
+ DeviceInfo(int id, std::string n = "", int i = 0, int o = 0): name(n), idx(id), in(i), out(o) {}
std::string desc() {
std::ostringstream oss;
oss << name << " (";
@@ -44,19 +44,27 @@ namespace portaudio {
return oss.str() + ")";
}
std::string name;
+ int idx;
int in, out;
};
typedef std::vector<DeviceInfo> DeviceInfos;
+ /// List of useless legacy devices of PortAudio that we want to omit...
+ static char const* g_ignored[] = { "front", "surround40", "surround41", "surround50", "surround51", "surround71", "iec958", "spdif", "dmix", NULL };
+
struct AudioDevices {
static int count() { return Pa_GetDeviceCount(); }
/// Constructor gets the PA devices into a vector
AudioDevices() {
for (unsigned i = 0, end = Pa_GetDeviceCount(); i != end; ++i) {
PaDeviceInfo const* info = Pa_GetDeviceInfo(i);
- if (!info) devices.push_back(DeviceInfo());
- else devices.push_back(DeviceInfo(convertToUTF8(info->name), info->maxInputChannels, info->maxOutputChannels));
+ if (!info) continue;
+ std::string name = convertToUTF8(info->name);
+ for (unsigned j = 0; g_ignored[j] && !name.empty(); ++j) {
+ if (name.find(g_ignored[j]) != std::string::npos) name.clear();
+ }
+ if (!name.empty()) devices.push_back(DeviceInfo(i, name, info->maxInputChannels, info->maxOutputChannels));
}
}
/// Get a printable dump of the devices
diff --git a/game/screen_audiodevices.cc b/game/screen_audiodevices.cc
index 0d667f7..6bff77b 100644
--- a/game/screen_audiodevices.cc
+++ b/game/screen_audiodevices.cc
@@ -34,8 +34,6 @@ void ScreenAudioDevices::enter() {
m_theme.reset(new ThemeAudioDevices());
portaudio::AudioDevices ads;
m_devs = ads.devices;
- // FIXME: Uncomment to test how different amount of devices behave
- //m_devs.resize(4);
// FIXME: Something more elegant, like a warning box
if (m_devs.empty()) throw std::runtime_error("No audio devices found!");
m_selected_column = 0;
@@ -162,7 +160,7 @@ bool ScreenAudioDevices::save(bool skip_ui_config) {
for (size_t d = 0; d < m_devs.size(); ++d) {
std::string mics = "", pdev = "";
for (size_t m = 0; m < m_mics.size(); ++m) {
- if (m_mics[m].dev == d) {
+ if (m_mics[m].dev == m_devs[d].idx) {
if (m_mics[m].name == "OUT") pdev = "out=2"; // Pdev, only stereo supported
else { // Mic
if (!mics.empty()) mics += ","; // Add separator if needed
|