mirror of
https://github.com/cookiengineer/audacity
synced 2025-10-18 00:21:18 +02:00
re-add and update device preferences to match new device model.
also restores portaudio device defaults functionality when the device can't be found.
This commit is contained in:
@@ -1,212 +1,252 @@
|
|||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|
||||||
Audacity - A Digital Audio Editor
|
Audacity - A Digital Audio Editor
|
||||||
Copyright 1999-2010 Audacity Team
|
Copyright 1999-2010 Audacity Team
|
||||||
Michael Chinen
|
Michael Chinen
|
||||||
|
|
||||||
******************************************************************/
|
******************************************************************/
|
||||||
|
|
||||||
#include "portaudio.h"
|
#include "portaudio.h"
|
||||||
#include "portmixer.h"
|
#include "portmixer.h"
|
||||||
|
|
||||||
#include "Audacity.h"
|
#include "Audacity.h"
|
||||||
// For compilers that support precompilation, includes "wx/wx.h".
|
// For compilers that support precompilation, includes "wx/wx.h".
|
||||||
#include <wx/wxprec.h>
|
#include <wx/wxprec.h>
|
||||||
|
|
||||||
#ifndef WX_PRECOMP
|
#ifndef WX_PRECOMP
|
||||||
#include <wx/choice.h>
|
#include <wx/choice.h>
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
#include <wx/intl.h>
|
#include <wx/intl.h>
|
||||||
#include <wx/settings.h>
|
#include <wx/settings.h>
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/statbmp.h>
|
#include <wx/statbmp.h>
|
||||||
#include <wx/tooltip.h>
|
#include <wx/tooltip.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Project.h"
|
#include "Project.h"
|
||||||
|
|
||||||
#include "AudioIO.h"
|
#include "AudioIO.h"
|
||||||
|
|
||||||
#include "DeviceManager.h"
|
#include "DeviceManager.h"
|
||||||
#include "toolbars/DeviceToolBar.h"
|
#include "toolbars/DeviceToolBar.h"
|
||||||
|
|
||||||
DeviceManager DeviceManager::dm;
|
DeviceManager DeviceManager::dm;
|
||||||
|
|
||||||
/// Gets the singleton instance
|
/// Gets the singleton instance
|
||||||
DeviceManager* DeviceManager::Instance()
|
DeviceManager* DeviceManager::Instance()
|
||||||
|
{
|
||||||
|
return &dm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases memory assosiated with the singleton
|
||||||
|
void DeviceManager::Destroy()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DeviceSourceMap> &DeviceManager::GetInputDeviceMaps()
|
||||||
|
{
|
||||||
|
if (!m_inited)
|
||||||
|
Init();
|
||||||
|
return mInputDeviceSourceMaps;
|
||||||
|
}
|
||||||
|
std::vector<DeviceSourceMap> &DeviceManager::GetOutputDeviceMaps()
|
||||||
|
{
|
||||||
|
if (!m_inited)
|
||||||
|
Init();
|
||||||
|
return mOutputDeviceSourceMaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wxString MakeDeviceSourceString(DeviceSourceMap *map)
|
||||||
|
{
|
||||||
|
wxString ret;
|
||||||
|
ret = map->deviceString;
|
||||||
|
if (map->totalSources > 1)
|
||||||
|
ret += wxString(": ", wxConvLocal) + map->sourceString;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceSourceMap* DeviceManager::GetDefaultDevice(int hostIndex, int isInput)
|
||||||
|
{
|
||||||
|
if (hostIndex < 0 || hostIndex >= Pa_GetHostApiCount()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct PaHostApiInfo *apiinfo = Pa_GetHostApiInfo(hostIndex); // get info on API
|
||||||
|
std::vector<DeviceSourceMap> & maps = isInput ? mInputDeviceSourceMaps : mOutputDeviceSourceMaps;
|
||||||
|
size_t i;
|
||||||
|
int targetDevice = isInput ? apiinfo->defaultInputDevice : apiinfo->defaultOutputDevice;
|
||||||
|
|
||||||
|
for (i = 0; i < maps.size(); i++) {
|
||||||
|
if (maps[i].deviceIndex == targetDevice)
|
||||||
|
return &maps[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
wxLogDebug(wxT("GetDefaultDevice() no default device"));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceSourceMap* DeviceManager::GetDefaultOutputDevice(int hostIndex)
|
||||||
{
|
{
|
||||||
return &dm;
|
return GetDefaultDevice(hostIndex, 0);
|
||||||
}
|
}
|
||||||
|
DeviceSourceMap* DeviceManager::GetDefaultInputDevice(int hostIndex)
|
||||||
/// Releases memory assosiated with the singleton
|
|
||||||
void DeviceManager::Destroy()
|
|
||||||
{
|
{
|
||||||
|
return GetDefaultDevice(hostIndex, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<DeviceSourceMap> &DeviceManager::GetInputDeviceMaps()
|
//--------------- Device Enumeration --------------------------
|
||||||
{
|
|
||||||
if (!m_inited)
|
//Port Audio requires we open the stream with a callback or a lot of devices will fail
|
||||||
Init();
|
//as this means open in blocking mode, so we use a dummy one.
|
||||||
return mInputDeviceSourceMaps;
|
static int DummyPaStreamCallback(
|
||||||
}
|
const void *input, void *output,
|
||||||
std::vector<DeviceSourceMap> &DeviceManager::GetOutputDeviceMaps()
|
unsigned long frameCount,
|
||||||
{
|
const PaStreamCallbackTimeInfo* timeInfo,
|
||||||
if (!m_inited)
|
PaStreamCallbackFlags statusFlags,
|
||||||
Init();
|
void *userData )
|
||||||
return mOutputDeviceSourceMaps;
|
{
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
//--------------- Device Enumeration --------------------------
|
|
||||||
|
static void FillHostDeviceInfo(DeviceSourceMap *map, const PaDeviceInfo *info, int deviceIndex, int isInput)
|
||||||
//Port Audio requires we open the stream with a callback or a lot of devices will fail
|
{
|
||||||
//as this means open in blocking mode, so we use a dummy one.
|
wxString hostapiName(Pa_GetHostApiInfo(info->hostApi)->name, wxConvLocal);
|
||||||
static int DummyPaStreamCallback(
|
wxString infoName(info->name, wxConvLocal);
|
||||||
const void *input, void *output,
|
|
||||||
unsigned long frameCount,
|
map->deviceIndex = deviceIndex;
|
||||||
const PaStreamCallbackTimeInfo* timeInfo,
|
map->hostIndex = info->hostApi;
|
||||||
PaStreamCallbackFlags statusFlags,
|
map->deviceString = infoName;
|
||||||
void *userData )
|
map->hostString = hostapiName;
|
||||||
{
|
map->numChannels = isInput ? info->maxInputChannels : info->maxOutputChannels;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
static void AddSourcesFromStream(int deviceIndex, const PaDeviceInfo *info, std::vector<DeviceSourceMap> *maps, PaStream *stream)
|
||||||
static void FillHostDeviceInfo(DeviceSourceMap *map, const PaDeviceInfo *info, int deviceIndex, int isInput)
|
{
|
||||||
{
|
int i;
|
||||||
wxString hostapiName(Pa_GetHostApiInfo(info->hostApi)->name, wxConvLocal);
|
PxMixer *portMixer;
|
||||||
wxString infoName(info->name, wxConvLocal);
|
DeviceSourceMap map;
|
||||||
|
|
||||||
map->deviceIndex = deviceIndex;
|
map.sourceIndex = -1;
|
||||||
map->hostIndex = info->hostApi;
|
map.totalSources = 0;
|
||||||
map->deviceString = infoName;
|
// Only inputs have sources, so we call FillHostDeviceInfo with a 1 to indicate this
|
||||||
map->hostString = hostapiName;
|
FillHostDeviceInfo(&map, info, deviceIndex, 1);
|
||||||
map->numChannels = isInput ? info->maxInputChannels : info->maxOutputChannels;
|
portMixer = Px_OpenMixer(stream, 0);
|
||||||
}
|
if (!portMixer) {
|
||||||
|
maps->push_back(map);
|
||||||
static void AddSourcesFromStream(int deviceIndex, const PaDeviceInfo *info, std::vector<DeviceSourceMap> *maps, PaStream *stream)
|
return;
|
||||||
{
|
}
|
||||||
int i;
|
|
||||||
PxMixer *portMixer;
|
//if there is only one source, we don't need to concatenate the source
|
||||||
DeviceSourceMap map;
|
//or enumerate, because it is something meaningless like 'master'
|
||||||
|
//(as opposed to 'mic in' or 'line in'), and the user doesn't have any choice.
|
||||||
map.sourceIndex = -1;
|
//note that some devices have no input sources at all but are still valid.
|
||||||
map.totalSources = 0;
|
//the behavior we do is the same for 0 and 1 source cases.
|
||||||
// Only inputs have sources, so we call FillHostDeviceInfo with a 1 to indicate this
|
map.totalSources = Px_GetNumInputSources(portMixer);
|
||||||
FillHostDeviceInfo(&map, info, deviceIndex, 1);
|
|
||||||
portMixer = Px_OpenMixer(stream, 0);
|
if (map.totalSources <= 1) {
|
||||||
if (!portMixer) {
|
map.sourceIndex = 0;
|
||||||
maps->push_back(map);
|
maps->push_back(map);
|
||||||
return;
|
} else {
|
||||||
}
|
//open up a stream with the device so portmixer can get the info out of it.
|
||||||
|
for (i = 0; i < map.totalSources; i++) {
|
||||||
//if there is only one source, we don't need to concatenate the source
|
map.sourceIndex = i;
|
||||||
//or enumerate, because it is something meaningless like 'master'
|
map.sourceString = wxString(Px_GetInputSourceName(portMixer, i), wxConvLocal);
|
||||||
//(as opposed to 'mic in' or 'line in'), and the user doesn't have any choice.
|
maps->push_back(map);
|
||||||
//note that some devices have no input sources at all but are still valid.
|
}
|
||||||
//the behavior we do is the same for 0 and 1 source cases.
|
}
|
||||||
map.totalSources = Px_GetNumInputSources(portMixer);
|
Px_CloseMixer(portMixer);
|
||||||
|
}
|
||||||
if (map.totalSources <= 1) {
|
|
||||||
map.sourceIndex = 0;
|
static bool IsInputDeviceAMapperDevice(const PaDeviceInfo *info)
|
||||||
maps->push_back(map);
|
{
|
||||||
} else {
|
// For Windows only, portaudio returns the default mapper object
|
||||||
//open up a stream with the device so portmixer can get the info out of it.
|
// as the first index after a new hostApi index is detected (true for MME and DS)
|
||||||
for (i = 0; i < map.totalSources; i++) {
|
// this is a bit of a hack, but there's no other way to find out which device is a mapper,
|
||||||
map.sourceIndex = i;
|
// I've looked at string comparisons, but if the system is in a different language this breaks.
|
||||||
map.sourceString = wxString(Px_GetInputSourceName(portMixer, i), wxConvLocal);
|
#ifdef __WXMSW__
|
||||||
maps->push_back(map);
|
static int lastHostApiTypeId = -1;
|
||||||
}
|
int hostApiTypeId = Pa_GetHostApiInfo(info->hostApi)->type;
|
||||||
}
|
if(hostApiTypeId != lastHostApiTypeId &&
|
||||||
Px_CloseMixer(portMixer);
|
(hostApiTypeId == paMME || hostApiTypeId == paDirectSound)) {
|
||||||
}
|
lastHostApiTypeId = hostApiTypeId;
|
||||||
|
return true;
|
||||||
static bool IsInputDeviceAMapperDevice(const PaDeviceInfo *info)
|
}
|
||||||
{
|
#endif
|
||||||
// For Windows only, portaudio returns the default mapper object
|
|
||||||
// as the first index after a new hostApi index is detected (true for MME and DS)
|
return false;
|
||||||
// this is a bit of a hack, but there's no other way to find out which device is a mapper,
|
}
|
||||||
// I've looked at string comparisons, but if the system is in a different language this breaks.
|
|
||||||
#ifdef __WXMSW__
|
static void AddSources(int deviceIndex, int rate, std::vector<DeviceSourceMap> *maps, int isInput)
|
||||||
static int lastHostApiTypeId = -1;
|
{
|
||||||
int hostApiTypeId = Pa_GetHostApiInfo(info->hostApi)->type;
|
int error = 0;
|
||||||
if(hostApiTypeId != lastHostApiTypeId &&
|
DeviceSourceMap map;
|
||||||
(hostApiTypeId == paMME || hostApiTypeId == paDirectSound)) {
|
const PaDeviceInfo *info = Pa_GetDeviceInfo(deviceIndex);
|
||||||
lastHostApiTypeId = hostApiTypeId;
|
|
||||||
return true;
|
// This tries to open the device with the samplerate worked out above, which
|
||||||
}
|
// will be the highest available for play and record on the device, or
|
||||||
#endif
|
// 44.1kHz if the info cannot be fetched.
|
||||||
|
|
||||||
return false;
|
PaStream *stream = NULL;
|
||||||
}
|
|
||||||
|
PaStreamParameters parameters;
|
||||||
static void AddSources(int deviceIndex, int rate, std::vector<DeviceSourceMap> *maps, int isInput)
|
|
||||||
{
|
parameters.device = deviceIndex;
|
||||||
int error = 0;
|
parameters.sampleFormat = paFloat32;
|
||||||
DeviceSourceMap map;
|
parameters.hostApiSpecificStreamInfo = NULL;
|
||||||
const PaDeviceInfo *info = Pa_GetDeviceInfo(deviceIndex);
|
parameters.channelCount = 1;
|
||||||
|
|
||||||
// This tries to open the device with the samplerate worked out above, which
|
// If the device is for input, open a stream so we can use portmixer to query
|
||||||
// will be the highest available for play and record on the device, or
|
// the number of inputs. We skip this for outputs because there are no 'sources'
|
||||||
// 44.1kHz if the info cannot be fetched.
|
// and some platforms (e.g. XP) have the same device for input and output, (while
|
||||||
|
// Vista/Win7 seperate these into two devices with the same names (but different
|
||||||
PaStream *stream = NULL;
|
// portaudio indecies)
|
||||||
|
// Also, for mapper devices we don't want to keep any sources, so check for it here
|
||||||
PaStreamParameters parameters;
|
if (isInput && !IsInputDeviceAMapperDevice(info)) {
|
||||||
|
if (info)
|
||||||
parameters.device = deviceIndex;
|
parameters.suggestedLatency = info->defaultLowInputLatency;
|
||||||
parameters.sampleFormat = paFloat32;
|
else
|
||||||
parameters.hostApiSpecificStreamInfo = NULL;
|
parameters.suggestedLatency = 10.0;
|
||||||
parameters.channelCount = 1;
|
|
||||||
|
error = Pa_OpenStream(&stream,
|
||||||
// If the device is for input, open a stream so we can use portmixer to query
|
¶meters,
|
||||||
// the number of inputs. We skip this for outputs because there are no 'sources'
|
NULL,
|
||||||
// and some platforms (e.g. XP) have the same device for input and output, (while
|
rate, paFramesPerBufferUnspecified,
|
||||||
// Vista/Win7 seperate these into two devices with the same names (but different
|
paClipOff | paDitherOff,
|
||||||
// portaudio indecies)
|
DummyPaStreamCallback, NULL);
|
||||||
// Also, for mapper devices we don't want to keep any sources, so check for it here
|
}
|
||||||
if (isInput && !IsInputDeviceAMapperDevice(info)) {
|
|
||||||
if (info)
|
if (stream && !error) {
|
||||||
parameters.suggestedLatency = info->defaultLowInputLatency;
|
AddSourcesFromStream(deviceIndex, info, maps, stream);
|
||||||
else
|
Pa_CloseStream(stream);
|
||||||
parameters.suggestedLatency = 10.0;
|
} else {
|
||||||
|
map.sourceIndex = -1;
|
||||||
error = Pa_OpenStream(&stream,
|
map.totalSources = 0;
|
||||||
¶meters,
|
FillHostDeviceInfo(&map, info, deviceIndex, isInput);
|
||||||
NULL,
|
maps->push_back(map);
|
||||||
rate, paFramesPerBufferUnspecified,
|
}
|
||||||
paClipOff | paDitherOff,
|
|
||||||
DummyPaStreamCallback, NULL);
|
if(error) {
|
||||||
}
|
wxLogDebug(wxT("PortAudio stream error creating device list: ") +
|
||||||
|
map.hostString + wxT(":") + map.deviceString + wxT(": ") +
|
||||||
if (stream && !error) {
|
wxString(Pa_GetErrorText( (PaError)error), wxConvLocal));
|
||||||
AddSourcesFromStream(deviceIndex, info, maps, stream);
|
}
|
||||||
Pa_CloseStream(stream);
|
}
|
||||||
} else {
|
|
||||||
map.sourceIndex = -1;
|
|
||||||
map.totalSources = 0;
|
/// Gets a new list of devices by terminating and restarting portaudio
|
||||||
FillHostDeviceInfo(&map, info, deviceIndex, isInput);
|
/// Assumes that DeviceManager is only used on the main thread.
|
||||||
maps->push_back(map);
|
void DeviceManager::Rescan()
|
||||||
}
|
{
|
||||||
|
// get rid of the previous scan info
|
||||||
if(error) {
|
this->mInputDeviceSourceMaps.clear();
|
||||||
wxLogDebug(wxT("PortAudio stream error creating device list: ") +
|
this->mOutputDeviceSourceMaps.clear();
|
||||||
map.hostString + wxT(":") + map.deviceString + wxT(": ") +
|
|
||||||
wxString(Pa_GetErrorText( (PaError)error), wxConvLocal));
|
// if we are doing a second scan then restart portaudio to get new devices
|
||||||
}
|
if (m_inited) {
|
||||||
}
|
// check to see if there is a stream open - can happen if monitoring,
|
||||||
|
|
||||||
|
|
||||||
/// Gets a new list of devices by terminating and restarting portaudio
|
|
||||||
/// Assumes that DeviceManager is only used on the main thread.
|
|
||||||
void DeviceManager::Rescan()
|
|
||||||
{
|
|
||||||
// get rid of the previous scan info
|
|
||||||
this->mInputDeviceSourceMaps.clear();
|
|
||||||
this->mOutputDeviceSourceMaps.clear();
|
|
||||||
|
|
||||||
// if we are doing a second scan then restart portaudio to get new devices
|
|
||||||
if (m_inited) {
|
|
||||||
// check to see if there is a stream open - can happen if monitoring,
|
|
||||||
// but otherwise Rescan() should not be available to the user.
|
// but otherwise Rescan() should not be available to the user.
|
||||||
if (gAudioIO) {
|
if (gAudioIO) {
|
||||||
if (gAudioIO->IsMonitoring())
|
if (gAudioIO->IsMonitoring())
|
||||||
@@ -216,54 +256,54 @@ void DeviceManager::Rescan()
|
|||||||
wxMilliSleep(100);
|
wxMilliSleep(100);
|
||||||
}
|
}
|
||||||
gAudioIO->HandleDeviceChange();
|
gAudioIO->HandleDeviceChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
// restart portaudio - this updates the device list
|
// restart portaudio - this updates the device list
|
||||||
Pa_Terminate();
|
Pa_Terminate();
|
||||||
Pa_Initialize();
|
Pa_Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
int nDevices = Pa_GetDeviceCount();
|
int nDevices = Pa_GetDeviceCount();
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
//The heirarchy for devices is Host/device/source.
|
//The heirarchy for devices is Host/device/source.
|
||||||
//Some newer systems aggregate this.
|
//Some newer systems aggregate this.
|
||||||
//So we need to call port mixer for every device to get the sources
|
//So we need to call port mixer for every device to get the sources
|
||||||
for (i = 0; i < nDevices; i++) {
|
for (i = 0; i < nDevices; i++) {
|
||||||
const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
|
const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
|
||||||
if (info->maxOutputChannels > 0) {
|
if (info->maxOutputChannels > 0) {
|
||||||
AddSources(i, info->defaultSampleRate, &mOutputDeviceSourceMaps, 0);
|
AddSources(i, info->defaultSampleRate, &mOutputDeviceSourceMaps, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->maxInputChannels > 0) {
|
if (info->maxInputChannels > 0) {
|
||||||
AddSources(i, info->defaultSampleRate, &mInputDeviceSourceMaps, 1);
|
AddSources(i, info->defaultSampleRate, &mInputDeviceSourceMaps, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this was not an initial scan update each device toolbar.
|
// If this was not an initial scan update each device toolbar.
|
||||||
// Hosts may have disappeared or appeared so a complete repopulate is needed.
|
// Hosts may have disappeared or appeared so a complete repopulate is needed.
|
||||||
if (m_inited) {
|
if (m_inited) {
|
||||||
DeviceToolBar *dt;
|
DeviceToolBar *dt;
|
||||||
for (size_t i = 0; i < gAudacityProjects.GetCount(); i++) {
|
for (size_t i = 0; i < gAudacityProjects.GetCount(); i++) {
|
||||||
dt = gAudacityProjects[i]->GetDeviceToolBar();
|
dt = gAudacityProjects[i]->GetDeviceToolBar();
|
||||||
dt->RefillCombos();
|
dt->RefillCombos();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_inited = true;
|
m_inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//private constructor - Singleton.
|
//private constructor - Singleton.
|
||||||
DeviceManager::DeviceManager()
|
DeviceManager::DeviceManager()
|
||||||
{
|
{
|
||||||
m_inited = false;
|
m_inited = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceManager::~DeviceManager()
|
DeviceManager::~DeviceManager()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceManager::Init()
|
void DeviceManager::Init()
|
||||||
{
|
{
|
||||||
Rescan();
|
Rescan();
|
||||||
}
|
}
|
||||||
|
@@ -32,6 +32,8 @@ typedef struct DeviceSourceMap {
|
|||||||
wxString hostString;
|
wxString hostString;
|
||||||
} DeviceSourceMap;
|
} DeviceSourceMap;
|
||||||
|
|
||||||
|
wxString MakeDeviceSourceString(DeviceSourceMap *map);
|
||||||
|
|
||||||
class DeviceManager
|
class DeviceManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -45,6 +47,9 @@ class DeviceManager
|
|||||||
/// Assumes that DeviceManager is only used on the main thread.
|
/// Assumes that DeviceManager is only used on the main thread.
|
||||||
void Rescan();
|
void Rescan();
|
||||||
|
|
||||||
|
DeviceSourceMap* GetDefaultOutputDevice(int hostIndex);
|
||||||
|
DeviceSourceMap* GetDefaultInputDevice(int hostIndex);
|
||||||
|
|
||||||
std::vector<DeviceSourceMap> &GetInputDeviceMaps();
|
std::vector<DeviceSourceMap> &GetInputDeviceMaps();
|
||||||
std::vector<DeviceSourceMap> &GetOutputDeviceMaps();
|
std::vector<DeviceSourceMap> &GetOutputDeviceMaps();
|
||||||
|
|
||||||
@@ -55,6 +60,8 @@ class DeviceManager
|
|||||||
/// Does an initial scan.
|
/// Does an initial scan.
|
||||||
/// Called by GetInputDeviceMaps and GetOutputDeviceMaps when needed.
|
/// Called by GetInputDeviceMaps and GetOutputDeviceMaps when needed.
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
|
DeviceSourceMap* GetDefaultDevice(int hostIndex, int isInput);
|
||||||
|
|
||||||
bool m_inited;
|
bool m_inited;
|
||||||
|
|
||||||
|
@@ -36,6 +36,7 @@ other settings.
|
|||||||
#include "../Internat.h"
|
#include "../Internat.h"
|
||||||
#include "../Prefs.h"
|
#include "../Prefs.h"
|
||||||
#include "../ShuttleGui.h"
|
#include "../ShuttleGui.h"
|
||||||
|
#include "../DeviceManager.h"
|
||||||
|
|
||||||
#include "DevicePrefs.h"
|
#include "DevicePrefs.h"
|
||||||
|
|
||||||
@@ -69,6 +70,7 @@ void DevicePrefs::Populate()
|
|||||||
// Get current setting for devices
|
// Get current setting for devices
|
||||||
mPlayDevice = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
|
mPlayDevice = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
|
||||||
mRecordDevice = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
|
mRecordDevice = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
|
||||||
|
mRecordSource = gPrefs->Read(wxT("/AudioIO/RecordingSource"), wxT(""));
|
||||||
mRecordChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2L);
|
mRecordChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2L);
|
||||||
|
|
||||||
//------------------------- Main section --------------------
|
//------------------------- Main section --------------------
|
||||||
@@ -187,35 +189,40 @@ void DevicePrefs::OnHost(wxCommandEvent & e)
|
|||||||
mHost->SetSelection(0);
|
mHost->SetSelection(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
mPlay->Clear();
|
std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
|
||||||
mRecord->Clear();
|
std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
|
||||||
|
|
||||||
wxArrayString playnames;
|
wxArrayString playnames;
|
||||||
wxArrayString recordnames;
|
wxArrayString recordnames;
|
||||||
|
size_t i;
|
||||||
int devindex; /* temp variable to hold the numeric ID of each device in turn */
|
int devindex; /* temp variable to hold the numeric ID of each device in turn */
|
||||||
|
wxString device;
|
||||||
|
wxString recDevice;
|
||||||
|
|
||||||
for (int i = 0; i < nDevices; i++) {
|
recDevice = mRecordDevice;
|
||||||
const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
|
if (this->mRecordSource != wxT(""))
|
||||||
if (info->hostApi == index) { /* if the device is for the current HostAPI */
|
recDevice += wxString(": ", wxConvLocal) + mRecordSource;
|
||||||
wxString name(info->name, wxConvLocal);
|
|
||||||
wxString device = DeviceName(info);
|
|
||||||
|
|
||||||
|
mRecord->Clear();
|
||||||
if (info->maxOutputChannels > 0) {
|
for (i = 0; i < inMaps.size(); i++) {
|
||||||
playnames.Add(name);
|
if (index == inMaps[i].hostIndex) {
|
||||||
devindex = mPlay->Append(name, (void *) info);
|
device = MakeDeviceSourceString(&inMaps[i]);
|
||||||
if (device == mPlayDevice) { /* if this is the default device, select it */
|
devindex = mRecord->Append(device);
|
||||||
mPlay->SetSelection(devindex);
|
mRecord->SetClientData(devindex, &inMaps[i]);
|
||||||
}
|
if (device == recDevice) { /* if this is the default device, select it */
|
||||||
|
mRecord->SetSelection(devindex);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (info->maxInputChannels > 0) {
|
mPlay->Clear();
|
||||||
recordnames.Add(name);
|
for (i = 0; i < outMaps.size(); i++) {
|
||||||
devindex = mRecord->Append(name, (void *) info);
|
if (index == outMaps[i].hostIndex) {
|
||||||
if (device == mRecordDevice) {
|
device = MakeDeviceSourceString(&outMaps[i]);
|
||||||
mRecord->SetSelection(devindex);
|
devindex = mPlay->Append(device);
|
||||||
}
|
mPlay->SetClientData(devindex, &outMaps[i]);
|
||||||
|
if (device == mPlayDevice) { /* if this is the default device, select it */
|
||||||
|
mPlay->SetSelection(devindex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,8 +243,9 @@ void DevicePrefs::OnHost(wxCommandEvent & e)
|
|||||||
* this API, as defined by PortAudio. We then fall back to using 0 only if
|
* this API, as defined by PortAudio. We then fall back to using 0 only if
|
||||||
* that fails */
|
* that fails */
|
||||||
if (mPlay->GetCount() && mPlay->GetSelection() == wxNOT_FOUND) {
|
if (mPlay->GetCount() && mPlay->GetSelection() == wxNOT_FOUND) {
|
||||||
wxLogDebug(wxT("DevicePrefs::OnHost(): no play device selected"));
|
DeviceSourceMap *defaultMap = DeviceManager::Instance()->GetDefaultOutputDevice(index);
|
||||||
mPlay->SetStringSelection(GetDefaultPlayDevice(index));
|
if (defaultMap)
|
||||||
|
mPlay->SetStringSelection(MakeDeviceSourceString(defaultMap));
|
||||||
|
|
||||||
if (mPlay->GetSelection() == wxNOT_FOUND) {
|
if (mPlay->GetSelection() == wxNOT_FOUND) {
|
||||||
mPlay->SetSelection(0);
|
mPlay->SetSelection(0);
|
||||||
@@ -245,8 +253,9 @@ void DevicePrefs::OnHost(wxCommandEvent & e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mRecord->GetCount() && mRecord->GetSelection() == wxNOT_FOUND) {
|
if (mRecord->GetCount() && mRecord->GetSelection() == wxNOT_FOUND) {
|
||||||
wxLogDebug(wxT("DevicePrefs::OnHost(): no record device selected"));
|
DeviceSourceMap *defaultMap = DeviceManager::Instance()->GetDefaultInputDevice(index);
|
||||||
mRecord->SetStringSelection(GetDefaultRecordDevice(index));
|
if (defaultMap)
|
||||||
|
mRecord->SetStringSelection(MakeDeviceSourceString(defaultMap));
|
||||||
|
|
||||||
if (mPlay->GetSelection() == wxNOT_FOUND) {
|
if (mPlay->GetSelection() == wxNOT_FOUND) {
|
||||||
mPlay->SetSelection(0);
|
mPlay->SetSelection(0);
|
||||||
@@ -254,8 +263,8 @@ void DevicePrefs::OnHost(wxCommandEvent & e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShuttleGui S(this, eIsCreating);
|
ShuttleGui S(this, eIsCreating);
|
||||||
S.SetSizeHints(mPlay, playnames);
|
S.SetSizeHints(mPlay, mPlay->GetStrings());
|
||||||
S.SetSizeHints(mRecord, recordnames);
|
S.SetSizeHints(mRecord, mRecord->GetStrings());
|
||||||
OnDevice(e);
|
OnDevice(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,9 +278,9 @@ void DevicePrefs::OnDevice(wxCommandEvent & e)
|
|||||||
int sel = mChannels->GetSelection();
|
int sel = mChannels->GetSelection();
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
||||||
const PaDeviceInfo *info = (const PaDeviceInfo *) mRecord->GetClientData(ndx);
|
DeviceSourceMap *inMap = (DeviceSourceMap *) mRecord->GetClientData(ndx);
|
||||||
if (info != NULL) {
|
if (inMap != NULL) {
|
||||||
cnt = info->maxInputChannels;
|
cnt = inMap->numChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sel != wxNOT_FOUND) {
|
if (sel != wxNOT_FOUND) {
|
||||||
@@ -324,67 +333,36 @@ void DevicePrefs::OnDevice(wxCommandEvent & e)
|
|||||||
Layout();
|
Layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString DevicePrefs::GetDefaultPlayDevice(int index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= Pa_GetHostApiCount()) {
|
|
||||||
return wxEmptyString;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct PaHostApiInfo *apiinfo = Pa_GetHostApiInfo(index); // get info on API
|
|
||||||
wxLogDebug(wxT("GetDefaultPlayDevice(): HostAPI index %d, name %s"), index, wxString(apiinfo->name, wxConvLocal).c_str());
|
|
||||||
wxLogDebug(wxT("GetDefaultPlayDevice() default output %d"), apiinfo->defaultOutputDevice);
|
|
||||||
const PaDeviceInfo* devinfo = Pa_GetDeviceInfo(apiinfo->defaultOutputDevice);
|
|
||||||
if (devinfo == NULL) {
|
|
||||||
wxLogDebug(wxT("GetDefaultPlayDevice() no default output device"));
|
|
||||||
return wxString("", wxConvLocal);
|
|
||||||
}
|
|
||||||
wxString name(devinfo->name, wxConvLocal);
|
|
||||||
wxLogDebug(wxT("GetDefaultPlayDevice() default output device name %s"), name.c_str());
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString DevicePrefs::GetDefaultRecordDevice(int index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= Pa_GetHostApiCount()) {
|
|
||||||
return wxEmptyString;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct PaHostApiInfo *apiinfo = Pa_GetHostApiInfo(index); // get info on API
|
|
||||||
wxLogDebug(wxT("GetDefaultRecordDevice(): HostAPI index %d, name %s"), index, wxString(apiinfo->name, wxConvLocal).c_str());
|
|
||||||
wxLogDebug(wxT("GetDefaultRecordDevice() default input %d"), apiinfo->defaultInputDevice);
|
|
||||||
const PaDeviceInfo* devinfo = Pa_GetDeviceInfo(apiinfo->defaultInputDevice);
|
|
||||||
if (devinfo == NULL) {
|
|
||||||
wxLogDebug(wxT("GetDefaultRecordDevice() no default input device"));
|
|
||||||
return wxString("", wxConvLocal);
|
|
||||||
}
|
|
||||||
wxString name(devinfo->name, wxConvLocal);
|
|
||||||
wxLogDebug(wxT("GetDefaultRecordDevice() default input device name %s"), name.c_str());
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DevicePrefs::Apply()
|
bool DevicePrefs::Apply()
|
||||||
{
|
{
|
||||||
ShuttleGui S(this, eIsSavingToPrefs);
|
ShuttleGui S(this, eIsSavingToPrefs);
|
||||||
PopulateOrExchange(S);
|
PopulateOrExchange(S);
|
||||||
|
DeviceSourceMap *map = NULL;
|
||||||
const PaDeviceInfo *info = NULL;
|
|
||||||
|
|
||||||
if (mPlay->GetCount() > 0) {
|
if (mPlay->GetCount() > 0) {
|
||||||
info = (const PaDeviceInfo *) mPlay->GetClientData(
|
map = (DeviceSourceMap *) mPlay->GetClientData(
|
||||||
mPlay->GetSelection());
|
mPlay->GetSelection());
|
||||||
}
|
}
|
||||||
if (info) {
|
if (map) {
|
||||||
gPrefs->Write(wxT("/AudioIO/PlaybackDevice"),
|
gPrefs->Write(wxT("/AudioIO/PlaybackDevice"), map->deviceString);
|
||||||
DeviceName(info));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info = NULL;
|
map = NULL;
|
||||||
if (mRecord->GetCount() > 0) {
|
if (mRecord->GetCount() > 0) {
|
||||||
info = (const PaDeviceInfo *) mRecord->GetClientData(mRecord->GetSelection());
|
map = (DeviceSourceMap *) mRecord->GetClientData(mRecord->GetSelection());
|
||||||
}
|
}
|
||||||
if (info) {
|
if (map) {
|
||||||
gPrefs->Write(wxT("/AudioIO/RecordingDevice"),
|
gPrefs->Write(wxT("/AudioIO/RecordingDevice"),
|
||||||
DeviceName(info));
|
map->deviceString);
|
||||||
|
gPrefs->Write(wxT("/AudioIO/RecordingSourceIndex"),
|
||||||
|
map->sourceIndex);
|
||||||
|
if (map->totalSources >= 1) {
|
||||||
|
gPrefs->Write(wxT("/AudioIO/RecordingSource"),
|
||||||
|
map->sourceString);
|
||||||
|
} else {
|
||||||
|
gPrefs->Write(wxT("/AudioIO/RecordingSource"),
|
||||||
|
wxT(""));
|
||||||
|
}
|
||||||
|
|
||||||
gPrefs->Write(wxT("/AudioIO/RecordChannels"),
|
gPrefs->Write(wxT("/AudioIO/RecordChannels"),
|
||||||
mChannels->GetSelection() + 1);
|
mChannels->GetSelection() + 1);
|
||||||
@@ -392,15 +370,3 @@ bool DevicePrefs::Apply()
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Indentation settings for Vim and Emacs and unique identifier for Arch, a
|
|
||||||
// version control system. Please do not modify past this point.
|
|
||||||
//
|
|
||||||
// Local Variables:
|
|
||||||
// c-basic-offset: 3
|
|
||||||
// indent-tabs-mode: nil
|
|
||||||
// End:
|
|
||||||
//
|
|
||||||
// vim: et sts=3 sw=3
|
|
||||||
// arch-tag: d6904b91-a320-4194-8d60-caa9175b6bb4
|
|
||||||
|
@@ -38,22 +38,12 @@ class DevicePrefs:public PrefsPanel
|
|||||||
void OnHost(wxCommandEvent & e);
|
void OnHost(wxCommandEvent & e);
|
||||||
void OnDevice(wxCommandEvent & e);
|
void OnDevice(wxCommandEvent & e);
|
||||||
|
|
||||||
/* @return The default playback device name for the selected HostAPI
|
|
||||||
*
|
|
||||||
* Created so we can set a default that respects the user's choice of API,
|
|
||||||
* unlike Pa_GetDefaultOutputDevice() which always returns the default
|
|
||||||
* device in the default API.
|
|
||||||
* @param index Which HostAPI in the lists mHostNames / mHostIndexes /
|
|
||||||
* mHostLabels the user has selected.
|
|
||||||
*/
|
|
||||||
wxString GetDefaultPlayDevice(int index);
|
|
||||||
wxString GetDefaultRecordDevice(int index);
|
|
||||||
|
|
||||||
wxArrayString mHostNames;
|
wxArrayString mHostNames;
|
||||||
wxArrayString mHostLabels;
|
wxArrayString mHostLabels;
|
||||||
|
|
||||||
wxString mPlayDevice;
|
wxString mPlayDevice;
|
||||||
wxString mRecordDevice;
|
wxString mRecordDevice;
|
||||||
|
wxString mRecordSource;
|
||||||
long mRecordChannels;
|
long mRecordChannels;
|
||||||
|
|
||||||
wxChoice *mHost;
|
wxChoice *mHost;
|
||||||
|
@@ -43,6 +43,7 @@
|
|||||||
#include "PrefsPanel.h"
|
#include "PrefsPanel.h"
|
||||||
|
|
||||||
#include "BatchPrefs.h"
|
#include "BatchPrefs.h"
|
||||||
|
#include "DevicePrefs.h"
|
||||||
#include "DirectoriesPrefs.h"
|
#include "DirectoriesPrefs.h"
|
||||||
#include "EffectsPrefs.h"
|
#include "EffectsPrefs.h"
|
||||||
#include "GUIPrefs.h"
|
#include "GUIPrefs.h"
|
||||||
@@ -88,6 +89,7 @@ PrefsDialog::PrefsDialog(wxWindow * parent)
|
|||||||
|
|
||||||
wxWindow *w;
|
wxWindow *w;
|
||||||
// Parameters are: AppPage( page, name, IsSelected, imageId)
|
// Parameters are: AppPage( page, name, IsSelected, imageId)
|
||||||
|
w = new DevicePrefs(mCategories); mCategories->AddPage(w, w->GetName(), false, 0);
|
||||||
w = new PlaybackPrefs(mCategories); mCategories->AddPage(w, w->GetName(), false, 0);
|
w = new PlaybackPrefs(mCategories); mCategories->AddPage(w, w->GetName(), false, 0);
|
||||||
w = new RecordingPrefs(mCategories); mCategories->AddPage(w, w->GetName(), false, 0);
|
w = new RecordingPrefs(mCategories); mCategories->AddPage(w, w->GetName(), false, 0);
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
|
@@ -80,16 +80,6 @@ void DeviceToolBar::RecreateTipWindows()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static wxString MakeDeviceSourceString(DeviceSourceMap *map)
|
|
||||||
{
|
|
||||||
wxString ret;
|
|
||||||
ret = map->deviceString;
|
|
||||||
if (map->totalSources > 1)
|
|
||||||
ret += wxString(": ", wxConvLocal) + map->sourceString;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeviceToolBar::DeinitChildren()
|
void DeviceToolBar::DeinitChildren()
|
||||||
{
|
{
|
||||||
mPlayBitmap = NULL;
|
mPlayBitmap = NULL;
|
||||||
@@ -264,7 +254,14 @@ void DeviceToolBar::UpdatePrefs()
|
|||||||
for (size_t i = 0; i < inMaps.size(); i++) {
|
for (size_t i = 0; i < inMaps.size(); i++) {
|
||||||
if (inMaps[i].hostString == hostName &&
|
if (inMaps[i].hostString == hostName &&
|
||||||
MakeDeviceSourceString(&inMaps[i]) == mInput->GetString(0)) {
|
MakeDeviceSourceString(&inMaps[i]) == mInput->GetString(0)) {
|
||||||
SetDevices(&inMaps[i], NULL);
|
// use the default. It should exist but check just in case, falling back on the 0 index.
|
||||||
|
DeviceSourceMap *defaultMap = DeviceManager::Instance()->GetDefaultInputDevice(inMaps[i].hostIndex);
|
||||||
|
if (defaultMap) {
|
||||||
|
mInput->SetStringSelection(MakeDeviceSourceString(defaultMap));
|
||||||
|
SetDevices(defaultMap, NULL);
|
||||||
|
} else {
|
||||||
|
SetDevices(&inMaps[i], NULL);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,7 +285,14 @@ void DeviceToolBar::UpdatePrefs()
|
|||||||
for (size_t i = 0; i < outMaps.size(); i++) {
|
for (size_t i = 0; i < outMaps.size(); i++) {
|
||||||
if (outMaps[i].hostString == hostName &&
|
if (outMaps[i].hostString == hostName &&
|
||||||
MakeDeviceSourceString(&outMaps[i]) == mOutput->GetString(0)) {
|
MakeDeviceSourceString(&outMaps[i]) == mOutput->GetString(0)) {
|
||||||
SetDevices(NULL, &outMaps[i]);
|
// use the default. It should exist but check just in case, falling back on the 0 index.
|
||||||
|
DeviceSourceMap *defaultMap = DeviceManager::Instance()->GetDefaultOutputDevice(inMaps[i].hostIndex);
|
||||||
|
if (defaultMap) {
|
||||||
|
mOutput->SetStringSelection(MakeDeviceSourceString(defaultMap));
|
||||||
|
SetDevices(NULL, defaultMap);
|
||||||
|
} else {
|
||||||
|
SetDevices(NULL, &outMaps[i]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user