From e68767cd042f565bbac662b2ff9586e224b51a12 Mon Sep 17 00:00:00 2001 From: mchinen Date: Sat, 19 Feb 2011 21:53:22 +0000 Subject: [PATCH] re-add and update device preferences to match new device model. also restores portaudio device defaults functionality when the device can't be found. --- src/DeviceManager.cpp | 540 ++++++++++++++++++--------------- src/DeviceManager.h | 7 + src/prefs/DevicePrefs.cpp | 146 ++++----- src/prefs/DevicePrefs.h | 12 +- src/prefs/PrefsDialog.cpp | 2 + src/toolbars/DeviceToolBar.cpp | 28 +- 6 files changed, 372 insertions(+), 363 deletions(-) diff --git a/src/DeviceManager.cpp b/src/DeviceManager.cpp index 0325b42ce..c9bf26f45 100644 --- a/src/DeviceManager.cpp +++ b/src/DeviceManager.cpp @@ -1,212 +1,252 @@ -/********************************************************************** - - Audacity - A Digital Audio Editor - Copyright 1999-2010 Audacity Team - Michael Chinen - -******************************************************************/ - -#include "portaudio.h" -#include "portmixer.h" - -#include "Audacity.h" -// For compilers that support precompilation, includes "wx/wx.h". -#include - -#ifndef WX_PRECOMP -#include -#include -#include -#include -#include -#include -#include -#endif - -#include "Project.h" - -#include "AudioIO.h" - -#include "DeviceManager.h" -#include "toolbars/DeviceToolBar.h" - -DeviceManager DeviceManager::dm; - -/// Gets the singleton instance -DeviceManager* DeviceManager::Instance() +/********************************************************************** + + Audacity - A Digital Audio Editor + Copyright 1999-2010 Audacity Team + Michael Chinen + +******************************************************************/ + +#include "portaudio.h" +#include "portmixer.h" + +#include "Audacity.h" +// For compilers that support precompilation, includes "wx/wx.h". +#include + +#ifndef WX_PRECOMP +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "Project.h" + +#include "AudioIO.h" + +#include "DeviceManager.h" +#include "toolbars/DeviceToolBar.h" + +DeviceManager DeviceManager::dm; + +/// Gets the singleton instance +DeviceManager* DeviceManager::Instance() +{ + return &dm; +} + +/// Releases memory assosiated with the singleton +void DeviceManager::Destroy() +{ + +} + +std::vector &DeviceManager::GetInputDeviceMaps() +{ + if (!m_inited) + Init(); + return mInputDeviceSourceMaps; +} +std::vector &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 & 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); } - -/// Releases memory assosiated with the singleton -void DeviceManager::Destroy() +DeviceSourceMap* DeviceManager::GetDefaultInputDevice(int hostIndex) { - + return GetDefaultDevice(hostIndex, 1); } - -std::vector &DeviceManager::GetInputDeviceMaps() -{ - if (!m_inited) - Init(); - return mInputDeviceSourceMaps; -} -std::vector &DeviceManager::GetOutputDeviceMaps() -{ - if (!m_inited) - Init(); - return mOutputDeviceSourceMaps; -} - -//--------------- Device Enumeration -------------------------- - -//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. -static int DummyPaStreamCallback( - const void *input, void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - return 0; -} - -static void FillHostDeviceInfo(DeviceSourceMap *map, const PaDeviceInfo *info, int deviceIndex, int isInput) -{ - wxString hostapiName(Pa_GetHostApiInfo(info->hostApi)->name, wxConvLocal); - wxString infoName(info->name, wxConvLocal); - - map->deviceIndex = deviceIndex; - map->hostIndex = info->hostApi; - map->deviceString = infoName; - map->hostString = hostapiName; - map->numChannels = isInput ? info->maxInputChannels : info->maxOutputChannels; -} - -static void AddSourcesFromStream(int deviceIndex, const PaDeviceInfo *info, std::vector *maps, PaStream *stream) -{ - int i; - PxMixer *portMixer; - DeviceSourceMap map; - - map.sourceIndex = -1; - map.totalSources = 0; - // Only inputs have sources, so we call FillHostDeviceInfo with a 1 to indicate this - FillHostDeviceInfo(&map, info, deviceIndex, 1); - portMixer = Px_OpenMixer(stream, 0); - if (!portMixer) { - maps->push_back(map); - return; - } - - //if there is only one source, we don't need to concatenate the source - //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. - //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); - - if (map.totalSources <= 1) { - map.sourceIndex = 0; - maps->push_back(map); - } else { - //open up a stream with the device so portmixer can get the info out of it. - for (i = 0; i < map.totalSources; i++) { - map.sourceIndex = i; - map.sourceString = wxString(Px_GetInputSourceName(portMixer, i), wxConvLocal); - maps->push_back(map); - } - } - Px_CloseMixer(portMixer); -} - -static bool IsInputDeviceAMapperDevice(const PaDeviceInfo *info) -{ - // 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) - // 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 int lastHostApiTypeId = -1; - int hostApiTypeId = Pa_GetHostApiInfo(info->hostApi)->type; - if(hostApiTypeId != lastHostApiTypeId && - (hostApiTypeId == paMME || hostApiTypeId == paDirectSound)) { - lastHostApiTypeId = hostApiTypeId; - return true; - } -#endif - - return false; -} - -static void AddSources(int deviceIndex, int rate, std::vector *maps, int isInput) -{ - int error = 0; - DeviceSourceMap map; - const PaDeviceInfo *info = Pa_GetDeviceInfo(deviceIndex); - - // 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 - // 44.1kHz if the info cannot be fetched. - - PaStream *stream = NULL; - - PaStreamParameters parameters; - - parameters.device = deviceIndex; - parameters.sampleFormat = paFloat32; - parameters.hostApiSpecificStreamInfo = NULL; - parameters.channelCount = 1; - - // If the device is for input, open a stream so we can use portmixer to query - // the number of inputs. We skip this for outputs because there are no 'sources' - // 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 - // portaudio indecies) - // Also, for mapper devices we don't want to keep any sources, so check for it here - if (isInput && !IsInputDeviceAMapperDevice(info)) { - if (info) - parameters.suggestedLatency = info->defaultLowInputLatency; - else - parameters.suggestedLatency = 10.0; - - error = Pa_OpenStream(&stream, - ¶meters, - NULL, - rate, paFramesPerBufferUnspecified, - paClipOff | paDitherOff, - DummyPaStreamCallback, NULL); - } - - if (stream && !error) { - AddSourcesFromStream(deviceIndex, info, maps, stream); - Pa_CloseStream(stream); - } else { - map.sourceIndex = -1; - map.totalSources = 0; - FillHostDeviceInfo(&map, info, deviceIndex, isInput); - maps->push_back(map); - } - - if(error) { - wxLogDebug(wxT("PortAudio stream error creating device list: ") + - map.hostString + wxT(":") + map.deviceString + wxT(": ") + - wxString(Pa_GetErrorText( (PaError)error), wxConvLocal)); - } -} - - -/// 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, + +//--------------- Device Enumeration -------------------------- + +//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. +static int DummyPaStreamCallback( + const void *input, void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + return 0; +} + +static void FillHostDeviceInfo(DeviceSourceMap *map, const PaDeviceInfo *info, int deviceIndex, int isInput) +{ + wxString hostapiName(Pa_GetHostApiInfo(info->hostApi)->name, wxConvLocal); + wxString infoName(info->name, wxConvLocal); + + map->deviceIndex = deviceIndex; + map->hostIndex = info->hostApi; + map->deviceString = infoName; + map->hostString = hostapiName; + map->numChannels = isInput ? info->maxInputChannels : info->maxOutputChannels; +} + +static void AddSourcesFromStream(int deviceIndex, const PaDeviceInfo *info, std::vector *maps, PaStream *stream) +{ + int i; + PxMixer *portMixer; + DeviceSourceMap map; + + map.sourceIndex = -1; + map.totalSources = 0; + // Only inputs have sources, so we call FillHostDeviceInfo with a 1 to indicate this + FillHostDeviceInfo(&map, info, deviceIndex, 1); + portMixer = Px_OpenMixer(stream, 0); + if (!portMixer) { + maps->push_back(map); + return; + } + + //if there is only one source, we don't need to concatenate the source + //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. + //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); + + if (map.totalSources <= 1) { + map.sourceIndex = 0; + maps->push_back(map); + } else { + //open up a stream with the device so portmixer can get the info out of it. + for (i = 0; i < map.totalSources; i++) { + map.sourceIndex = i; + map.sourceString = wxString(Px_GetInputSourceName(portMixer, i), wxConvLocal); + maps->push_back(map); + } + } + Px_CloseMixer(portMixer); +} + +static bool IsInputDeviceAMapperDevice(const PaDeviceInfo *info) +{ + // 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) + // 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 int lastHostApiTypeId = -1; + int hostApiTypeId = Pa_GetHostApiInfo(info->hostApi)->type; + if(hostApiTypeId != lastHostApiTypeId && + (hostApiTypeId == paMME || hostApiTypeId == paDirectSound)) { + lastHostApiTypeId = hostApiTypeId; + return true; + } +#endif + + return false; +} + +static void AddSources(int deviceIndex, int rate, std::vector *maps, int isInput) +{ + int error = 0; + DeviceSourceMap map; + const PaDeviceInfo *info = Pa_GetDeviceInfo(deviceIndex); + + // 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 + // 44.1kHz if the info cannot be fetched. + + PaStream *stream = NULL; + + PaStreamParameters parameters; + + parameters.device = deviceIndex; + parameters.sampleFormat = paFloat32; + parameters.hostApiSpecificStreamInfo = NULL; + parameters.channelCount = 1; + + // If the device is for input, open a stream so we can use portmixer to query + // the number of inputs. We skip this for outputs because there are no 'sources' + // 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 + // portaudio indecies) + // Also, for mapper devices we don't want to keep any sources, so check for it here + if (isInput && !IsInputDeviceAMapperDevice(info)) { + if (info) + parameters.suggestedLatency = info->defaultLowInputLatency; + else + parameters.suggestedLatency = 10.0; + + error = Pa_OpenStream(&stream, + ¶meters, + NULL, + rate, paFramesPerBufferUnspecified, + paClipOff | paDitherOff, + DummyPaStreamCallback, NULL); + } + + if (stream && !error) { + AddSourcesFromStream(deviceIndex, info, maps, stream); + Pa_CloseStream(stream); + } else { + map.sourceIndex = -1; + map.totalSources = 0; + FillHostDeviceInfo(&map, info, deviceIndex, isInput); + maps->push_back(map); + } + + if(error) { + wxLogDebug(wxT("PortAudio stream error creating device list: ") + + map.hostString + wxT(":") + map.deviceString + wxT(": ") + + wxString(Pa_GetErrorText( (PaError)error), wxConvLocal)); + } +} + + +/// 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. if (gAudioIO) { if (gAudioIO->IsMonitoring()) @@ -216,54 +256,54 @@ void DeviceManager::Rescan() wxMilliSleep(100); } gAudioIO->HandleDeviceChange(); - } - - // restart portaudio - this updates the device list - Pa_Terminate(); - Pa_Initialize(); - } - - int nDevices = Pa_GetDeviceCount(); - int i; - - //The heirarchy for devices is Host/device/source. - //Some newer systems aggregate this. - //So we need to call port mixer for every device to get the sources - for (i = 0; i < nDevices; i++) { - const PaDeviceInfo *info = Pa_GetDeviceInfo(i); - if (info->maxOutputChannels > 0) { - AddSources(i, info->defaultSampleRate, &mOutputDeviceSourceMaps, 0); - } - - if (info->maxInputChannels > 0) { - AddSources(i, info->defaultSampleRate, &mInputDeviceSourceMaps, 1); - } - } - - // If this was not an initial scan update each device toolbar. + } + + // restart portaudio - this updates the device list + Pa_Terminate(); + Pa_Initialize(); + } + + int nDevices = Pa_GetDeviceCount(); + int i; + + //The heirarchy for devices is Host/device/source. + //Some newer systems aggregate this. + //So we need to call port mixer for every device to get the sources + for (i = 0; i < nDevices; i++) { + const PaDeviceInfo *info = Pa_GetDeviceInfo(i); + if (info->maxOutputChannels > 0) { + AddSources(i, info->defaultSampleRate, &mOutputDeviceSourceMaps, 0); + } + + if (info->maxInputChannels > 0) { + AddSources(i, info->defaultSampleRate, &mInputDeviceSourceMaps, 1); + } + } + + // If this was not an initial scan update each device toolbar. // Hosts may have disappeared or appeared so a complete repopulate is needed. if (m_inited) { DeviceToolBar *dt; for (size_t i = 0; i < gAudacityProjects.GetCount(); i++) { dt = gAudacityProjects[i]->GetDeviceToolBar(); dt->RefillCombos(); - } - } - m_inited = true; -} - -//private constructor - Singleton. -DeviceManager::DeviceManager() -{ - m_inited = false; -} - -DeviceManager::~DeviceManager() -{ - -} - -void DeviceManager::Init() -{ - Rescan(); -} + } + } + m_inited = true; +} + +//private constructor - Singleton. +DeviceManager::DeviceManager() +{ + m_inited = false; +} + +DeviceManager::~DeviceManager() +{ + +} + +void DeviceManager::Init() +{ + Rescan(); +} diff --git a/src/DeviceManager.h b/src/DeviceManager.h index 90858d3af..937a5365b 100644 --- a/src/DeviceManager.h +++ b/src/DeviceManager.h @@ -32,6 +32,8 @@ typedef struct DeviceSourceMap { wxString hostString; } DeviceSourceMap; +wxString MakeDeviceSourceString(DeviceSourceMap *map); + class DeviceManager { public: @@ -45,6 +47,9 @@ class DeviceManager /// Assumes that DeviceManager is only used on the main thread. void Rescan(); + DeviceSourceMap* GetDefaultOutputDevice(int hostIndex); + DeviceSourceMap* GetDefaultInputDevice(int hostIndex); + std::vector &GetInputDeviceMaps(); std::vector &GetOutputDeviceMaps(); @@ -55,6 +60,8 @@ class DeviceManager /// Does an initial scan. /// Called by GetInputDeviceMaps and GetOutputDeviceMaps when needed. void Init(); + + DeviceSourceMap* GetDefaultDevice(int hostIndex, int isInput); bool m_inited; diff --git a/src/prefs/DevicePrefs.cpp b/src/prefs/DevicePrefs.cpp index a80123e76..d626b2361 100644 --- a/src/prefs/DevicePrefs.cpp +++ b/src/prefs/DevicePrefs.cpp @@ -36,6 +36,7 @@ other settings. #include "../Internat.h" #include "../Prefs.h" #include "../ShuttleGui.h" +#include "../DeviceManager.h" #include "DevicePrefs.h" @@ -69,6 +70,7 @@ void DevicePrefs::Populate() // Get current setting for devices mPlayDevice = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT("")); mRecordDevice = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT("")); + mRecordSource = gPrefs->Read(wxT("/AudioIO/RecordingSource"), wxT("")); mRecordChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2L); //------------------------- Main section -------------------- @@ -187,35 +189,40 @@ void DevicePrefs::OnHost(wxCommandEvent & e) mHost->SetSelection(0); } - mPlay->Clear(); - mRecord->Clear(); + std::vector &inMaps = DeviceManager::Instance()->GetInputDeviceMaps(); + std::vector &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps(); wxArrayString playnames; wxArrayString recordnames; - + size_t i; 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++) { - const PaDeviceInfo *info = Pa_GetDeviceInfo(i); - if (info->hostApi == index) { /* if the device is for the current HostAPI */ - wxString name(info->name, wxConvLocal); - wxString device = DeviceName(info); + recDevice = mRecordDevice; + if (this->mRecordSource != wxT("")) + recDevice += wxString(": ", wxConvLocal) + mRecordSource; - - if (info->maxOutputChannels > 0) { - playnames.Add(name); - devindex = mPlay->Append(name, (void *) info); - if (device == mPlayDevice) { /* if this is the default device, select it */ - mPlay->SetSelection(devindex); - } + mRecord->Clear(); + for (i = 0; i < inMaps.size(); i++) { + if (index == inMaps[i].hostIndex) { + device = MakeDeviceSourceString(&inMaps[i]); + devindex = mRecord->Append(device); + mRecord->SetClientData(devindex, &inMaps[i]); + if (device == recDevice) { /* if this is the default device, select it */ + mRecord->SetSelection(devindex); } + } + } - if (info->maxInputChannels > 0) { - recordnames.Add(name); - devindex = mRecord->Append(name, (void *) info); - if (device == mRecordDevice) { - mRecord->SetSelection(devindex); - } + mPlay->Clear(); + for (i = 0; i < outMaps.size(); i++) { + if (index == outMaps[i].hostIndex) { + device = MakeDeviceSourceString(&outMaps[i]); + 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 * that fails */ if (mPlay->GetCount() && mPlay->GetSelection() == wxNOT_FOUND) { - wxLogDebug(wxT("DevicePrefs::OnHost(): no play device selected")); - mPlay->SetStringSelection(GetDefaultPlayDevice(index)); + DeviceSourceMap *defaultMap = DeviceManager::Instance()->GetDefaultOutputDevice(index); + if (defaultMap) + mPlay->SetStringSelection(MakeDeviceSourceString(defaultMap)); if (mPlay->GetSelection() == wxNOT_FOUND) { mPlay->SetSelection(0); @@ -245,8 +253,9 @@ void DevicePrefs::OnHost(wxCommandEvent & e) } if (mRecord->GetCount() && mRecord->GetSelection() == wxNOT_FOUND) { - wxLogDebug(wxT("DevicePrefs::OnHost(): no record device selected")); - mRecord->SetStringSelection(GetDefaultRecordDevice(index)); + DeviceSourceMap *defaultMap = DeviceManager::Instance()->GetDefaultInputDevice(index); + if (defaultMap) + mRecord->SetStringSelection(MakeDeviceSourceString(defaultMap)); if (mPlay->GetSelection() == wxNOT_FOUND) { mPlay->SetSelection(0); @@ -254,8 +263,8 @@ void DevicePrefs::OnHost(wxCommandEvent & e) } ShuttleGui S(this, eIsCreating); - S.SetSizeHints(mPlay, playnames); - S.SetSizeHints(mRecord, recordnames); + S.SetSizeHints(mPlay, mPlay->GetStrings()); + S.SetSizeHints(mRecord, mRecord->GetStrings()); OnDevice(e); } @@ -269,9 +278,9 @@ void DevicePrefs::OnDevice(wxCommandEvent & e) int sel = mChannels->GetSelection(); int cnt = 0; - const PaDeviceInfo *info = (const PaDeviceInfo *) mRecord->GetClientData(ndx); - if (info != NULL) { - cnt = info->maxInputChannels; + DeviceSourceMap *inMap = (DeviceSourceMap *) mRecord->GetClientData(ndx); + if (inMap != NULL) { + cnt = inMap->numChannels; } if (sel != wxNOT_FOUND) { @@ -324,67 +333,36 @@ void DevicePrefs::OnDevice(wxCommandEvent & e) 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() { ShuttleGui S(this, eIsSavingToPrefs); PopulateOrExchange(S); - - const PaDeviceInfo *info = NULL; + DeviceSourceMap *map = NULL; if (mPlay->GetCount() > 0) { - info = (const PaDeviceInfo *) mPlay->GetClientData( + map = (DeviceSourceMap *) mPlay->GetClientData( mPlay->GetSelection()); } - if (info) { - gPrefs->Write(wxT("/AudioIO/PlaybackDevice"), - DeviceName(info)); + if (map) { + gPrefs->Write(wxT("/AudioIO/PlaybackDevice"), map->deviceString); } - info = NULL; + map = NULL; 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"), - 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"), mChannels->GetSelection() + 1); @@ -392,15 +370,3 @@ bool DevicePrefs::Apply() 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 diff --git a/src/prefs/DevicePrefs.h b/src/prefs/DevicePrefs.h index cedaf9b3c..2248d2e99 100644 --- a/src/prefs/DevicePrefs.h +++ b/src/prefs/DevicePrefs.h @@ -38,22 +38,12 @@ class DevicePrefs:public PrefsPanel void OnHost(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 mHostLabels; wxString mPlayDevice; wxString mRecordDevice; + wxString mRecordSource; long mRecordChannels; wxChoice *mHost; diff --git a/src/prefs/PrefsDialog.cpp b/src/prefs/PrefsDialog.cpp index 998455265..16092239e 100644 --- a/src/prefs/PrefsDialog.cpp +++ b/src/prefs/PrefsDialog.cpp @@ -43,6 +43,7 @@ #include "PrefsPanel.h" #include "BatchPrefs.h" +#include "DevicePrefs.h" #include "DirectoriesPrefs.h" #include "EffectsPrefs.h" #include "GUIPrefs.h" @@ -88,6 +89,7 @@ PrefsDialog::PrefsDialog(wxWindow * parent) wxWindow *w; // 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 RecordingPrefs(mCategories); mCategories->AddPage(w, w->GetName(), false, 0); #ifdef EXPERIMENTAL_MIDI_OUT diff --git a/src/toolbars/DeviceToolBar.cpp b/src/toolbars/DeviceToolBar.cpp index 7595061af..888f62dcf 100644 --- a/src/toolbars/DeviceToolBar.cpp +++ b/src/toolbars/DeviceToolBar.cpp @@ -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() { mPlayBitmap = NULL; @@ -264,7 +254,14 @@ void DeviceToolBar::UpdatePrefs() for (size_t i = 0; i < inMaps.size(); i++) { if (inMaps[i].hostString == hostName && 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; } } @@ -288,7 +285,14 @@ void DeviceToolBar::UpdatePrefs() for (size_t i = 0; i < outMaps.size(); i++) { if (outMaps[i].hostString == hostName && 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; } }