mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-29 06:59:27 +02:00
This is a workaround for the portaudio issue where changing the default device in xp will corrupt the portaudio device indecies. This combined with the portmixer fix (earlier today) should address bug 29.
779 lines
24 KiB
C++
779 lines
24 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
DeviceToolBar.cpp
|
|
|
|
Dominic Mazzoni
|
|
|
|
*******************************************************************//*!
|
|
|
|
\class DeviceToolBar
|
|
\brief A toobar to allow easier changing of input and output devices .
|
|
|
|
*//*******************************************************************/
|
|
|
|
|
|
#include "../Audacity.h"
|
|
|
|
// For compilers that support precompilation, includes "wx/wx.h".
|
|
#include <wx/wxprec.h>
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include <wx/choice.h>
|
|
#include <wx/event.h>
|
|
#include <wx/intl.h>
|
|
#include <wx/settings.h>
|
|
#include <wx/sizer.h>
|
|
#include <wx/statbmp.h>
|
|
#include <wx/tooltip.h>
|
|
#endif
|
|
|
|
#include "../AudacityApp.h"
|
|
|
|
#include "DeviceToolBar.h"
|
|
#include "ToolDock.h"
|
|
|
|
#include "../AColor.h"
|
|
#include "../AllThemeResources.h"
|
|
#include "../AudioIO.h"
|
|
#include "../ImageManipulation.h"
|
|
#include "../Prefs.h"
|
|
#include "../Project.h"
|
|
#include "../Theme.h"
|
|
#include "../widgets/Grabber.h"
|
|
#include "../DeviceManager.h"
|
|
|
|
IMPLEMENT_CLASS(DeviceToolBar, ToolBar);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
/// Methods for DeviceToolBar
|
|
////////////////////////////////////////////////////////////
|
|
|
|
BEGIN_EVENT_TABLE(DeviceToolBar, ToolBar)
|
|
EVT_CHOICE(wxID_ANY, DeviceToolBar::OnChoice)
|
|
EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, DeviceToolBar::OnCaptureKey)
|
|
END_EVENT_TABLE()
|
|
|
|
//Standard contructor
|
|
DeviceToolBar::DeviceToolBar()
|
|
: ToolBar(DeviceBarID, _("Device"), wxT("Device"), true)
|
|
{
|
|
}
|
|
|
|
DeviceToolBar::~DeviceToolBar()
|
|
{
|
|
delete mPlayBitmap;
|
|
delete mRecordBitmap;
|
|
}
|
|
|
|
void DeviceToolBar::Create(wxWindow *parent)
|
|
{
|
|
ToolBar::Create(parent);
|
|
|
|
// Simulate a size event to set initial meter placement/size
|
|
wxSizeEvent dummy;
|
|
OnSize(dummy);
|
|
}
|
|
|
|
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;
|
|
mRecordBitmap = NULL;
|
|
|
|
mInput = NULL;
|
|
mOutput = NULL;
|
|
mInputChannels = NULL;
|
|
mHost = NULL;
|
|
}
|
|
|
|
void DeviceToolBar::Populate()
|
|
{
|
|
DeinitChildren();
|
|
// Hosts
|
|
mHost = new wxChoice(this,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize);
|
|
mHost->SetName(_("Audio Host"));
|
|
|
|
Add(mHost, 0, wxALIGN_CENTER);
|
|
|
|
// Output device
|
|
mPlayBitmap = new wxBitmap(theTheme.Bitmap(bmpSpeaker));
|
|
Add(new wxStaticBitmap(this,
|
|
wxID_ANY,
|
|
*mPlayBitmap), 0, wxALIGN_CENTER);
|
|
|
|
mOutput = new wxChoice(this,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize);
|
|
mOutput->SetName(_("Output Device"));
|
|
Add(mOutput, 0, wxALIGN_CENTER);
|
|
|
|
// Input device
|
|
mRecordBitmap = new wxBitmap(theTheme.Bitmap(bmpMic));
|
|
|
|
Add(new wxStaticBitmap(this,
|
|
wxID_ANY,
|
|
*mRecordBitmap), 0, wxALIGN_CENTER);
|
|
|
|
mInput = new wxChoice(this,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize);
|
|
mInput->SetName(_("Input Device"));
|
|
Add(mInput, 0, wxALIGN_CENTER);
|
|
|
|
mInputChannels = new wxChoice(this,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize);
|
|
mInputChannels->SetName(_("Input Channels"));
|
|
Add(mInputChannels, 0, wxALIGN_CENTER);
|
|
|
|
mHost->Connect(wxEVT_SET_FOCUS,
|
|
wxFocusEventHandler(DeviceToolBar::OnFocus),
|
|
NULL,
|
|
this);
|
|
mHost->Connect(wxEVT_KILL_FOCUS,
|
|
wxFocusEventHandler(DeviceToolBar::OnFocus),
|
|
NULL,
|
|
this);
|
|
mOutput->Connect(wxEVT_SET_FOCUS,
|
|
wxFocusEventHandler(DeviceToolBar::OnFocus),
|
|
NULL,
|
|
this);
|
|
mOutput->Connect(wxEVT_KILL_FOCUS,
|
|
wxFocusEventHandler(DeviceToolBar::OnFocus),
|
|
NULL,
|
|
this);
|
|
mInput->Connect(wxEVT_SET_FOCUS,
|
|
wxFocusEventHandler(DeviceToolBar::OnFocus),
|
|
NULL,
|
|
this);
|
|
mInput->Connect(wxEVT_KILL_FOCUS,
|
|
wxFocusEventHandler(DeviceToolBar::OnFocus),
|
|
NULL,
|
|
this);
|
|
mInputChannels->Connect(wxEVT_SET_FOCUS,
|
|
wxFocusEventHandler(DeviceToolBar::OnFocus),
|
|
NULL,
|
|
this);
|
|
mInputChannels->Connect(wxEVT_KILL_FOCUS,
|
|
wxFocusEventHandler(DeviceToolBar::OnFocus),
|
|
NULL,
|
|
this);
|
|
|
|
RefillCombos();
|
|
}
|
|
|
|
void DeviceToolBar::RefillCombos()
|
|
{
|
|
FillHosts();
|
|
FillHostDevices();
|
|
FillInputChannels();
|
|
// make the device display selection reflect the prefs if they exist
|
|
UpdatePrefs();
|
|
}
|
|
|
|
void DeviceToolBar::OnFocus(wxFocusEvent &event)
|
|
{
|
|
wxCommandEvent e(EVT_CAPTURE_KEYBOARD);
|
|
|
|
if (event.GetEventType() == wxEVT_KILL_FOCUS) {
|
|
e.SetEventType(EVT_RELEASE_KEYBOARD);
|
|
}
|
|
e.SetEventObject(this);
|
|
GetParent()->GetEventHandler()->ProcessEvent(e);
|
|
|
|
Refresh(false);
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void DeviceToolBar::OnCaptureKey(wxCommandEvent &event)
|
|
{
|
|
wxKeyEvent *kevent = (wxKeyEvent *)event.GetEventObject();
|
|
int keyCode = kevent->GetKeyCode();
|
|
|
|
// Pass UP/DOWN/LEFT/RIGHT through for input/output choice
|
|
if (FindFocus() == mOutput && (keyCode == WXK_LEFT || keyCode == WXK_RIGHT
|
|
|| keyCode == WXK_UP || keyCode == WXK_DOWN)) {
|
|
return;
|
|
}
|
|
if (FindFocus() == mInput && (keyCode == WXK_LEFT || keyCode == WXK_RIGHT
|
|
|| keyCode == WXK_UP || keyCode == WXK_DOWN)) {
|
|
return;
|
|
}
|
|
|
|
event.Skip();
|
|
|
|
return;
|
|
}
|
|
|
|
void DeviceToolBar::UpdatePrefs()
|
|
{
|
|
wxString hostName;
|
|
wxString devName;
|
|
wxString sourceName;
|
|
wxString desc;
|
|
std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
|
|
std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
|
|
|
|
|
|
int hostSelectionIndex = mHost->GetSelection();
|
|
wxString oldHost = hostSelectionIndex >= 0 ? mHost->GetString(hostSelectionIndex) :
|
|
wxT("");
|
|
hostName = gPrefs->Read(wxT("/AudioIO/Host"), wxT(""));
|
|
|
|
// if the prefs host name doesn't match the one displayed, it changed
|
|
// in another project's DeviceToolBar, so we need to repopulate everything.
|
|
if (oldHost != hostName)
|
|
FillHostDevices();
|
|
|
|
devName = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
|
|
sourceName = gPrefs->Read(wxT("/AudioIO/RecordingSource"), wxT(""));
|
|
if (sourceName == wxT(""))
|
|
desc = devName;
|
|
else
|
|
desc = devName + wxString(": ", wxConvLocal) + sourceName;
|
|
|
|
if (mInput->GetStringSelection() != desc &&
|
|
mInput->FindString(desc) != wxNOT_FOUND) {
|
|
mInput->SetStringSelection(desc);
|
|
FillInputChannels();
|
|
} else if (mInput->GetStringSelection() != desc && mInput->GetCount()) {
|
|
//use the 0th index if we have no familiar devices
|
|
mInput->SetSelection(0);
|
|
for (size_t i = 0; i < inMaps.size(); i++) {
|
|
if (inMaps[i].hostString == hostName &&
|
|
MakeDeviceSourceString(&inMaps[i]) == mInput->GetString(0)) {
|
|
SetDevices(&inMaps[i], NULL);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
devName = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
|
|
sourceName = gPrefs->Read(wxT("/AudioIO/PlaybackSource"), wxT(""));
|
|
if (sourceName == wxT(""))
|
|
desc = devName;
|
|
else
|
|
desc = devName + wxString(": ", wxConvLocal) + sourceName;
|
|
mOutput->SetStringSelection(desc);
|
|
|
|
if (mOutput->GetStringSelection() != desc &&
|
|
mOutput->FindString(desc) != wxNOT_FOUND) {
|
|
mOutput->SetStringSelection(desc);
|
|
} else if (mOutput->GetStringSelection() != desc &&
|
|
mOutput->GetCount()) {
|
|
//use the 0th index if we have no familiar devices
|
|
mOutput->SetSelection(0);
|
|
for (size_t i = 0; i < outMaps.size(); i++) {
|
|
if (outMaps[i].hostString == hostName &&
|
|
MakeDeviceSourceString(&outMaps[i]) == mOutput->GetString(0)) {
|
|
SetDevices(NULL, &outMaps[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
long oldChannels = 1, newChannels;
|
|
oldChannels = mInputChannels->GetSelection() + 1;
|
|
gPrefs->Read(wxT("/AudioIO/RecordChannels"), &newChannels, 0);
|
|
if (newChannels > 0 && oldChannels != newChannels)
|
|
mInputChannels->SetSelection(newChannels - 1);
|
|
|
|
mHost->SetStringSelection(hostName);
|
|
|
|
RegenerateTooltips();
|
|
|
|
// Set label to pull in language change
|
|
SetLabel(_("Device"));
|
|
|
|
// Give base class a chance
|
|
ToolBar::UpdatePrefs();
|
|
|
|
Layout();
|
|
Refresh();
|
|
}
|
|
|
|
|
|
void DeviceToolBar::EnableDisableButtons()
|
|
{
|
|
if (gAudioIO) {
|
|
// we allow changes when monitoring, but not when recording
|
|
bool audioStreamActive = gAudioIO->IsStreamActive() && !gAudioIO->IsMonitoring();
|
|
mHost->Enable(!audioStreamActive);
|
|
mInput->Enable(!audioStreamActive);
|
|
mOutput->Enable(!audioStreamActive);
|
|
mInputChannels->Enable(!audioStreamActive);
|
|
}
|
|
}
|
|
|
|
void DeviceToolBar::RegenerateTooltips()
|
|
{
|
|
#if wxUSE_TOOLTIPS
|
|
mOutput->SetToolTip(_("Output Device"));
|
|
mInput->SetToolTip(_("Input Device"));
|
|
mHost->SetToolTip(_("Audio Host"));
|
|
mInputChannels->SetToolTip(_("Input Channels"));
|
|
#endif
|
|
}
|
|
|
|
bool DeviceToolBar::Layout()
|
|
{
|
|
bool ret;
|
|
RepositionCombos();
|
|
ret = ToolBar::Layout();
|
|
return ret;
|
|
}
|
|
|
|
// returns true if the combo is constrained and false otherwise
|
|
// @param toolbarWidth the width of the toolbar in pixels
|
|
// @param ratio an in/out for the desired and resultant width ratio.
|
|
// @param flex the amount of extra space allowed to have past the available.
|
|
// the amount used is subtracted.
|
|
static bool RepositionCombo(wxWindow *combo, int toolbarWidth, wxSize desiredSize,
|
|
float &ratio, float &flex, int marginPixels, bool changesRatio)
|
|
{
|
|
float ratioChange;
|
|
bool constrained = false;
|
|
|
|
// push margin pixels
|
|
desiredSize.x += marginPixels;
|
|
|
|
// truncate the window size if necessary
|
|
if (desiredSize.x > toolbarWidth * (flex + ratio)) {
|
|
constrained = true;
|
|
desiredSize.SetWidth(toolbarWidth * (flex + ratio));
|
|
if (desiredSize.GetWidth() - marginPixels < 0)
|
|
desiredSize.SetWidth(marginPixels);
|
|
}
|
|
|
|
// keep track of how much space gained or lost so it can be used by other combos.
|
|
if (changesRatio) {
|
|
ratioChange = (desiredSize.x / ((float) toolbarWidth)) - ratio;
|
|
ratio += ratioChange;
|
|
flex -= ratioChange;
|
|
}
|
|
|
|
// pop the margin pixels
|
|
desiredSize.x -= marginPixels;
|
|
|
|
combo->SetMinSize(desiredSize);
|
|
combo->SetMaxSize(desiredSize);
|
|
|
|
return constrained;
|
|
}
|
|
|
|
//These don't add up to 1 because there is a bit of margin that we allow
|
|
//the layout sizer to handle.
|
|
#define kHostWidthRatio 0.13f
|
|
#define kInputWidthRatio 0.32f
|
|
#define kOutputWidthRatio 0.32f
|
|
#define kChannelsWidthRatio 0.18f
|
|
|
|
void DeviceToolBar::RepositionCombos()
|
|
{
|
|
int w, h, dockw, dockh;
|
|
float ratioUnused;
|
|
bool constrained = true;
|
|
wxWindow *window;
|
|
wxSize desiredInput, desiredOutput, desiredHost, desiredChannels;
|
|
float hostRatio, outputRatio, inputRatio, channelsRatio;
|
|
// if the toolbar is docked then the width we should use is the project width.
|
|
// as the toolbar's with can extend past this.
|
|
GetClientSize(&w, &h);
|
|
if (IsDocked()) {
|
|
// If the toolbar is docked its width can be larger than what is actually viewable
|
|
// So take the min. We don't need to worry about having another toolbar to the left off us
|
|
// because if we are larger than the dock size we always get our own row.
|
|
// and if smaller then we don't use the dock size (because we take the min).
|
|
window = GetDock();
|
|
window->GetClientSize(&dockw, &dockh);
|
|
if (dockw < w)
|
|
w = dockw;
|
|
}
|
|
// subtract the main grabber on the left and the resizer as well
|
|
w -= grabberWidth + GetResizeGrabberWidth();
|
|
if (w <= 0)
|
|
return;
|
|
|
|
// set up initial sizes and ratios
|
|
hostRatio = kHostWidthRatio;
|
|
inputRatio = kInputWidthRatio;
|
|
outputRatio = kOutputWidthRatio;
|
|
channelsRatio = kChannelsWidthRatio;
|
|
|
|
desiredHost = mHost->GetBestSize();
|
|
desiredInput = mInput->GetBestSize();
|
|
desiredOutput = mOutput->GetBestSize();
|
|
desiredChannels = mInputChannels->GetBestSize();
|
|
|
|
// wxGtk has larger comboboxes than the other platforms. For DeviceToolBar this will cause
|
|
// the height to be double because of the discrete grid layout. So we shrink it to prevent this.
|
|
#ifdef __WXGTK__
|
|
desiredHost.SetHeight(desiredHost.GetHeight() -4);
|
|
desiredInput.SetHeight(desiredHost.GetHeight());
|
|
desiredOutput.SetHeight(desiredHost.GetHeight());
|
|
desiredChannels.SetHeight(desiredHost.GetHeight());
|
|
#endif
|
|
|
|
ratioUnused = 0.995f - (kHostWidthRatio + kInputWidthRatio + kOutputWidthRatio + kChannelsWidthRatio);
|
|
int i = 0;
|
|
// limit the amount of times we solve contraints to 5
|
|
while (constrained && ratioUnused > 0.01f && i < 5) {
|
|
i++;
|
|
constrained = false;
|
|
|
|
constrained = RepositionCombo(mHost, w, desiredHost, hostRatio, ratioUnused,
|
|
0, true) || constrained;
|
|
constrained = RepositionCombo(mInput, w, desiredInput, inputRatio, ratioUnused,
|
|
mRecordBitmap->GetWidth(), true) || constrained;
|
|
constrained = RepositionCombo(mOutput, w, desiredOutput, outputRatio, ratioUnused,
|
|
mPlayBitmap->GetWidth(), true) || constrained;
|
|
constrained = RepositionCombo(mInputChannels, w, desiredChannels, channelsRatio, ratioUnused,
|
|
0, true) || constrained;
|
|
}
|
|
|
|
Update();
|
|
}
|
|
|
|
void DeviceToolBar::FillHosts()
|
|
{
|
|
wxArrayString hosts;
|
|
size_t i;
|
|
|
|
std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
|
|
std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
|
|
// go over our lists add the host to the list if it isn't there yet
|
|
for (i = 0; i < inMaps.size(); i++)
|
|
if (hosts.Index(inMaps[i].hostString) == wxNOT_FOUND)
|
|
hosts.Add(inMaps[i].hostString);
|
|
for (i = 0; i < outMaps.size(); i++)
|
|
if (hosts.Index(outMaps[i].hostString) == wxNOT_FOUND)
|
|
hosts.Add(outMaps[i].hostString);
|
|
|
|
mHost->Clear();
|
|
mHost->Append(hosts);
|
|
|
|
if (hosts.GetCount() == 0)
|
|
mHost->Enable(false);
|
|
}
|
|
|
|
void DeviceToolBar::FillHostDevices()
|
|
{
|
|
std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
|
|
std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
|
|
|
|
//read what is in the prefs
|
|
wxString host = gPrefs->Read(wxT("/AudioIO/Host"), wxT(""));
|
|
size_t i;
|
|
int foundHostIndex = -1;
|
|
|
|
// if the host is not in the hosts combo then we rescanned.
|
|
// set it to blank so we search for another host.
|
|
if (mHost->FindString(host) == wxNOT_FOUND)
|
|
host = wxT("");
|
|
|
|
for (i = 0; i < outMaps.size(); i++) {
|
|
if (outMaps[i].hostString == host) {
|
|
foundHostIndex = outMaps[i].hostIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (foundHostIndex == -1) {
|
|
for (i = 0; i < inMaps.size(); i++) {
|
|
if (inMaps[i].hostString == host) {
|
|
foundHostIndex = inMaps[i].hostIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If no host was found based on the prefs device host, load the first available one
|
|
if (foundHostIndex == -1) {
|
|
if (outMaps.size())
|
|
foundHostIndex = outMaps[0].hostIndex;
|
|
else if (inMaps.size())
|
|
foundHostIndex = inMaps[0].hostIndex;
|
|
}
|
|
|
|
// If we still have no host it means no devices, in which case do nothing.
|
|
if (foundHostIndex == -1)
|
|
return;
|
|
|
|
// Repopulate the Input/Output device list available to the user
|
|
mInput->Clear();
|
|
for (i = 0; i < inMaps.size(); i++) {
|
|
if (foundHostIndex == inMaps[i].hostIndex) {
|
|
mInput->Append(MakeDeviceSourceString(&inMaps[i]));
|
|
if (host == wxT("")) {
|
|
host = inMaps[i].hostString;
|
|
gPrefs->Write(wxT("/AudioIO/Host"), host);
|
|
mHost->SetStringSelection(host);
|
|
}
|
|
}
|
|
}
|
|
mInput->Enable(mInput->GetCount() ? true : false);
|
|
|
|
mOutput->Clear();
|
|
for (i = 0; i < outMaps.size(); i++) {
|
|
if (foundHostIndex == outMaps[i].hostIndex) {
|
|
mOutput->Append(MakeDeviceSourceString(&outMaps[i]));
|
|
if (host == wxT("")) {
|
|
host = outMaps[i].hostString;
|
|
gPrefs->Write(wxT("/AudioIO/Host"), host);
|
|
mHost->SetStringSelection(host);
|
|
}
|
|
}
|
|
}
|
|
mOutput->Enable(mOutput->GetCount() ? true : false);
|
|
|
|
// The setting of the Device is left up to OnChoice
|
|
}
|
|
|
|
//return 1 if host changed, 0 otherwise.
|
|
int DeviceToolBar::ChangeHost()
|
|
{
|
|
int hostSelectionIndex;
|
|
hostSelectionIndex = mHost->GetSelection();
|
|
|
|
wxString oldHost = gPrefs->Read(wxT("/AudioIO/Host"), wxT(""));
|
|
wxString newHost = hostSelectionIndex >= 0 ? mHost->GetString(hostSelectionIndex) :
|
|
oldHost;
|
|
|
|
if (oldHost == newHost)
|
|
return 0;
|
|
|
|
//change the host and switch to correct devices.
|
|
gPrefs->Write(wxT("/AudioIO/Host"), newHost);
|
|
// populate the devices
|
|
FillHostDevices();
|
|
|
|
return 1;
|
|
}
|
|
|
|
void DeviceToolBar::FillInputChannels()
|
|
{
|
|
std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
|
|
wxString host = gPrefs->Read(wxT("/AudioIO/Host"), wxT(""));
|
|
wxString device = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
|
|
wxString source = gPrefs->Read(wxT("/AudioIO/RecordingSource"), wxT(""));
|
|
long oldChannels = 1, newChannels;
|
|
|
|
gPrefs->Read(wxT("/AudioIO/RecordChannels"), &oldChannels);
|
|
int index = -1;
|
|
size_t i, j;
|
|
mInputChannels->Clear();
|
|
for (i = 0; i < inMaps.size(); i++) {
|
|
if (source == inMaps[i].sourceString &&
|
|
device == inMaps[i].deviceString &&
|
|
host == inMaps[i].hostString) {
|
|
|
|
// add one selection for each channel of this source
|
|
for (j = 0; j < (unsigned int) inMaps[i].numChannels; j++) {
|
|
wxString name;
|
|
|
|
if (j == 0) {
|
|
name = _("1 (Mono) Input Channel");
|
|
}
|
|
else if (j == 1) {
|
|
name = _("2 (Stereo) Input Channels");
|
|
}
|
|
else {
|
|
name = wxString::Format(wxT("%d"), j + 1);
|
|
}
|
|
mInputChannels->Append(name);
|
|
}
|
|
newChannels = inMaps[i].numChannels;
|
|
if (oldChannels < newChannels && oldChannels >= 1)
|
|
newChannels = oldChannels;
|
|
if (newChannels >= 1)
|
|
mInputChannels->SetSelection(newChannels - 1);
|
|
gPrefs->Write(wxT("/AudioIO/RecordChannels"), newChannels);
|
|
mInputChannels->Enable(mInputChannels->GetCount() ? true : false);
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
if (index == -1)
|
|
mInputChannels->Enable(false);
|
|
}
|
|
void DeviceToolBar::SetDevices(DeviceSourceMap *in, DeviceSourceMap *out)
|
|
{
|
|
if (in) {
|
|
gPrefs->Write(wxT("/AudioIO/RecordingDevice"), in->deviceString);
|
|
gPrefs->Write(wxT("/AudioIO/RecordingSourceIndex"), in->sourceIndex);
|
|
if (in->totalSources >= 1) {
|
|
gPrefs->Write(wxT("/AudioIO/RecordingSource"), in->sourceString);
|
|
} else {
|
|
gPrefs->Write(wxT("/AudioIO/RecordingSource"), wxT(""));
|
|
}
|
|
FillInputChannels();
|
|
}
|
|
|
|
if (out) {
|
|
gPrefs->Write(wxT("/AudioIO/PlaybackDevice"), out->deviceString);
|
|
if (out->totalSources >= 1) {
|
|
gPrefs->Write(wxT("/AudioIO/PlaybackSource"), out->sourceString);
|
|
} else {
|
|
gPrefs->Write(wxT("/AudioIO/PlaybackSource"), wxT(""));
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeviceToolBar::ChangeDevice(bool isInput)
|
|
{
|
|
int newIndex = -1;
|
|
wxChoice *combo = isInput ? mInput :mOutput;
|
|
size_t i;
|
|
|
|
int selectionIndex = mInput->GetSelection();
|
|
std::vector<DeviceSourceMap> &maps = isInput ? DeviceManager::Instance()->GetInputDeviceMaps()
|
|
: DeviceManager::Instance()->GetOutputDeviceMaps();
|
|
|
|
wxString host = gPrefs->Read(wxT("/AudioIO/Host"), wxT(""));
|
|
// Find device indices for input and output
|
|
if (selectionIndex >= 0 ) {
|
|
wxString newDevice = combo->GetStringSelection();
|
|
for (i = 0; i < maps.size(); ++i) {
|
|
wxString name;
|
|
name = MakeDeviceSourceString(&maps[i]);
|
|
if (name == newDevice && maps[i].hostString == host) {
|
|
newIndex = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newIndex < 0) {
|
|
wxLogDebug(wxT("DeviceToolBar::OnChoice(): couldn't find device indices"));
|
|
return;
|
|
}
|
|
|
|
SetDevices(isInput ? &maps[newIndex] : NULL,
|
|
isInput ? NULL : &maps[newIndex]);
|
|
}
|
|
|
|
void DeviceToolBar::OnChoice(wxCommandEvent &event)
|
|
{
|
|
wxObject *eventObject = event.GetEventObject();
|
|
//if we've changed hosts, we've handled the device switching already.
|
|
if (eventObject == mHost) {
|
|
ChangeHost();
|
|
} else if (eventObject == mInputChannels) {
|
|
int channelsSelectionIndex = mInputChannels->GetSelection();
|
|
if (channelsSelectionIndex >= 0)
|
|
gPrefs->Write(wxT("/AudioIO/RecordChannels"),channelsSelectionIndex + 1);
|
|
} else if (eventObject == mInput) {
|
|
ChangeDevice(true);
|
|
}
|
|
else if (eventObject == mOutput) {
|
|
ChangeDevice(false);
|
|
}
|
|
|
|
if (gAudioIO) {
|
|
// We cannot have gotten here if gAudioIO->IsAudioTokenActive(),
|
|
// per the setting of AudioIONotBusyFlag and AudioIOBusyFlag in
|
|
// AudacityProject::GetUpdateFlags().
|
|
// However, we can have an invalid audio token (so IsAudioTokenActive()
|
|
// is false), but be monitoring.
|
|
// If monitoring, have to stop the stream, so HandleDeviceChange() can work.
|
|
// We could disable the Preferences command while monitoring, i.e.,
|
|
// set AudioIONotBusyFlag/AudioIOBusyFlag according to monitoring, as well.
|
|
// Instead allow it because unlike recording, for example, monitoring
|
|
// is not clearly something that should prohibit changing device.
|
|
// TO-DO: We *could* be smarter in this method and call HandleDeviceChange()
|
|
// only when the device choices actually changed. True of lots of prefs!
|
|
// As is, we always stop monitoring before handling the device change.
|
|
if (gAudioIO->IsMonitoring())
|
|
{
|
|
gAudioIO->StopStream();
|
|
while (gAudioIO->IsBusy())
|
|
wxMilliSleep(100);
|
|
}
|
|
gAudioIO->HandleDeviceChange();
|
|
}
|
|
|
|
// Update all projects' DeviceToolBar.
|
|
for (size_t i = 0; i < gAudacityProjects.GetCount(); i++) {
|
|
gAudacityProjects[i]->GetDeviceToolBar()->UpdatePrefs();
|
|
}
|
|
}
|
|
|
|
void DeviceToolBar::ShowInputDialog()
|
|
{
|
|
ShowComboDialog(mInput, wxString(_("Select Input Device")));
|
|
}
|
|
void DeviceToolBar::ShowOutputDialog()
|
|
{
|
|
ShowComboDialog(mOutput, wxString(_("Select Output Device")));
|
|
}
|
|
void DeviceToolBar::ShowHostDialog()
|
|
{
|
|
ShowComboDialog(mHost, wxString(_("Select Audio Host")));
|
|
}
|
|
void DeviceToolBar::ShowChannelsDialog()
|
|
{
|
|
ShowComboDialog(mInputChannels, wxString(_("Select Input Channels")));
|
|
}
|
|
|
|
void DeviceToolBar::ShowComboDialog(wxChoice *combo, const wxString &title)
|
|
{
|
|
if (!combo || combo->GetCount() == 0) {
|
|
wxMessageBox(_("Device information is not available."));
|
|
return;
|
|
}
|
|
|
|
#if USE_PORTMIXER
|
|
wxArrayString inputSources = combo->GetStrings();
|
|
|
|
wxDialog dlg(NULL, wxID_ANY, title);
|
|
ShuttleGui S(&dlg, eIsCreating);
|
|
wxChoice *c;
|
|
|
|
S.StartVerticalLay(true);
|
|
{
|
|
S.StartHorizontalLay(wxCENTER, false);
|
|
{
|
|
c = S.AddChoice(combo->GetName(),
|
|
combo->GetStringSelection(),
|
|
&inputSources);
|
|
}
|
|
S.EndHorizontalLay();
|
|
S.AddStandardButtons();
|
|
}
|
|
S.EndVerticalLay();
|
|
|
|
dlg.SetSize(dlg.GetSizer()->GetMinSize());
|
|
dlg.Center();
|
|
|
|
if (dlg.ShowModal() == wxID_OK)
|
|
{
|
|
wxCommandEvent dummyEvent;
|
|
dummyEvent.SetEventObject(combo);
|
|
// SetSelection() doesn't send an event, so we call OnChoice explicitly
|
|
combo->SetSelection(c->GetSelection());
|
|
OnChoice(dummyEvent);
|
|
}
|
|
#endif
|
|
}
|