1
0
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:
mchinen
2011-02-19 21:53:22 +00:00
parent 77b1be7f3d
commit e68767cd04
6 changed files with 372 additions and 363 deletions

View File

@@ -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 &parameters,
// 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;
&parameters, 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();
} }

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
} }
} }