760 lines
23 KiB
C++
760 lines
23 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 "DeviceToolBar.h"
|
|
#include "ToolManager.h"
|
|
|
|
// For compilers that support precompilation, includes "wx/wx.h".
|
|
#include <wx/wxprec.h>
|
|
|
|
#include <wx/setup.h> // for wxUSE_* macros
|
|
|
|
#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/stattext.h>
|
|
#include <wx/tooltip.h>
|
|
#endif
|
|
|
|
#include "../TrackPanel.h"
|
|
|
|
#include "../AColor.h"
|
|
#include "../AllThemeResources.h"
|
|
#include "../AudioIOBase.h"
|
|
#include "../ImageManipulation.h"
|
|
#include "../KeyboardCapture.h"
|
|
#include "../Prefs.h"
|
|
#include "../Project.h"
|
|
#include "../ShuttleGui.h"
|
|
#include "../widgets/Grabber.h"
|
|
#include "../DeviceManager.h"
|
|
#include "../widgets/AudacityMessageBox.h"
|
|
#include "../widgets/Grabber.h"
|
|
|
|
#if wxUSE_ACCESSIBILITY
|
|
#include "../widgets/WindowAccessible.h"
|
|
#endif
|
|
|
|
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()
|
|
|
|
static int DeviceToolbarPrefsID()
|
|
{
|
|
static int value = wxNewId();
|
|
return value;
|
|
}
|
|
|
|
//Standard constructor
|
|
DeviceToolBar::DeviceToolBar( AudacityProject &project )
|
|
: ToolBar( project, DeviceBarID, XO("Device"), wxT("Device"), true )
|
|
{
|
|
wxTheApp->Bind( EVT_RESCANNED_DEVICES,
|
|
&DeviceToolBar::OnRescannedDevices, this );
|
|
}
|
|
|
|
DeviceToolBar::~DeviceToolBar()
|
|
{
|
|
}
|
|
|
|
DeviceToolBar &DeviceToolBar::Get( AudacityProject &project )
|
|
{
|
|
auto &toolManager = ToolManager::Get( project );
|
|
return *static_cast<DeviceToolBar*>( toolManager.GetToolBar(DeviceBarID) );
|
|
}
|
|
|
|
const DeviceToolBar &DeviceToolBar::Get( const AudacityProject &project )
|
|
{
|
|
return Get( const_cast<AudacityProject&>( project )) ;
|
|
}
|
|
|
|
void DeviceToolBar::Create(wxWindow *parent)
|
|
{
|
|
ToolBar::Create(parent);
|
|
|
|
// Simulate a size event to set initial meter placement/size
|
|
wxSizeEvent event(GetSize(), GetId());
|
|
event.SetEventObject(this);
|
|
GetEventHandler()->ProcessEvent(event);
|
|
}
|
|
|
|
void DeviceToolBar::DeinitChildren()
|
|
{
|
|
mInput = NULL;
|
|
mOutput = NULL;
|
|
mInputChannels = NULL;
|
|
mHost = NULL;
|
|
}
|
|
|
|
void DeviceToolBar::Populate()
|
|
{
|
|
SetBackgroundColour( theTheme.Colour( clrMedium ) );
|
|
DeinitChildren();
|
|
|
|
// Hosts
|
|
mHost = safenew wxChoice(this,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize);
|
|
#if wxUSE_ACCESSIBILITY
|
|
// so that name can be set on a standard control
|
|
mHost->SetAccessible(safenew WindowAccessible(mHost));
|
|
#endif
|
|
Add(mHost, 15, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 1);
|
|
|
|
// Input device
|
|
Add(safenew AStaticBitmap(this,
|
|
wxID_ANY,
|
|
theTheme.Bitmap(bmpMic)), 0, wxALIGN_CENTER_VERTICAL);
|
|
mInput = safenew wxChoice(this,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize);
|
|
#if wxUSE_ACCESSIBILITY
|
|
// so that name can be set on a standard control
|
|
mInput->SetAccessible(safenew WindowAccessible(mInput));
|
|
#endif
|
|
Add(mInput, 30, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 1);
|
|
|
|
// Input channels
|
|
mInputChannels = safenew wxChoice(this,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize);
|
|
#if wxUSE_ACCESSIBILITY
|
|
// so that name can be set on a standard control
|
|
mInputChannels->SetAccessible(safenew WindowAccessible(mInputChannels));
|
|
#endif
|
|
Add(mInputChannels, 20, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 1);
|
|
|
|
// Output device
|
|
Add(safenew AStaticBitmap(this,
|
|
wxID_ANY,
|
|
theTheme.Bitmap(bmpSpeaker)), 0, wxALIGN_CENTER_VERTICAL);
|
|
mOutput = safenew wxChoice(this,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize);
|
|
#if wxUSE_ACCESSIBILITY
|
|
// so that name can be set on a standard control
|
|
mOutput->SetAccessible(safenew WindowAccessible(mOutput));
|
|
#endif
|
|
Add(mOutput, 30, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 1);
|
|
|
|
#if defined(__WXGTK3__)
|
|
// Nothing special
|
|
#elif defined(__WXGTK__)
|
|
// Scale the font to fit inside (hopefully)
|
|
wxFont font = mHost->GetFont();
|
|
font.Scale((double) toolbarSingle / mHost->GetSize().GetHeight());
|
|
|
|
// Set it
|
|
mHost->SetFont(font);
|
|
mInput->SetFont(font);
|
|
mInputChannels->SetFont(font);
|
|
mOutput->SetFont(font);
|
|
#endif
|
|
|
|
mHost->Bind(wxEVT_SET_FOCUS,
|
|
&DeviceToolBar::OnFocus,
|
|
this);
|
|
mHost->Bind(wxEVT_KILL_FOCUS,
|
|
&DeviceToolBar::OnFocus,
|
|
this);
|
|
mOutput->Bind(wxEVT_SET_FOCUS,
|
|
&DeviceToolBar::OnFocus,
|
|
this);
|
|
mOutput->Bind(wxEVT_KILL_FOCUS,
|
|
&DeviceToolBar::OnFocus,
|
|
this);
|
|
mInput->Bind(wxEVT_SET_FOCUS,
|
|
&DeviceToolBar::OnFocus,
|
|
this);
|
|
mInput->Bind(wxEVT_KILL_FOCUS,
|
|
&DeviceToolBar::OnFocus,
|
|
this);
|
|
mInputChannels->Bind(wxEVT_SET_FOCUS,
|
|
&DeviceToolBar::OnFocus,
|
|
this);
|
|
mInputChannels->Bind(wxEVT_KILL_FOCUS,
|
|
&DeviceToolBar::OnFocus,
|
|
this);
|
|
|
|
SetNames();
|
|
|
|
RefillCombos();
|
|
}
|
|
|
|
void DeviceToolBar::OnFocus(wxFocusEvent &event)
|
|
{
|
|
KeyboardCapture::OnFocus( *this, event );
|
|
}
|
|
|
|
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() == mInput && (keyCode == WXK_LEFT || keyCode == WXK_RIGHT
|
|
|| keyCode == WXK_UP || keyCode == WXK_DOWN)) {
|
|
return;
|
|
}
|
|
|
|
if (FindFocus() == mOutput && (keyCode == WXK_LEFT || keyCode == WXK_RIGHT
|
|
|| keyCode == WXK_UP || keyCode == WXK_DOWN)) {
|
|
return;
|
|
}
|
|
event.Skip();
|
|
|
|
return;
|
|
}
|
|
|
|
void DeviceToolBar::UpdatePrefs()
|
|
{
|
|
wxString desc;
|
|
const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
|
|
const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
|
|
|
|
|
|
int hostSelectionIndex = mHost->GetSelection();
|
|
wxString oldHost = hostSelectionIndex >= 0 ? mHost->GetString(hostSelectionIndex) :
|
|
wxString{};
|
|
auto hostName = AudioIOHost.Read();
|
|
|
|
// 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();
|
|
|
|
auto devName = AudioIORecordingDevice.Read();
|
|
auto sourceName = AudioIORecordingSource.Read();
|
|
if (sourceName.empty())
|
|
desc = devName;
|
|
else
|
|
desc = devName + wxT(": ") + sourceName;
|
|
|
|
if (mInput->GetStringSelection() != desc &&
|
|
mInput->FindString(desc) != wxNOT_FOUND) {
|
|
mInput->SetStringSelection(desc);
|
|
FillInputChannels();
|
|
} else if (mInput->GetStringSelection() != desc && mInput->GetCount()) {
|
|
for (size_t i = 0; i < inMaps.size(); i++) {
|
|
if (inMaps[i].hostString == hostName &&
|
|
MakeDeviceSourceString(&inMaps[i]) == mInput->GetString(0)) {
|
|
// 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 {
|
|
//use the first item (0th index) if we have no familiar devices
|
|
mInput->SetSelection(0);
|
|
SetDevices(&inMaps[i], NULL);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
devName = AudioIOPlaybackDevice.Read();
|
|
sourceName = gPrefs->Read(wxT("/AudioIO/PlaybackSource"), wxT(""));
|
|
if (sourceName.empty())
|
|
desc = devName;
|
|
else
|
|
desc = devName + wxT(": ") + sourceName;
|
|
|
|
if (mOutput->GetStringSelection() != desc &&
|
|
mOutput->FindString(desc) != wxNOT_FOUND) {
|
|
mOutput->SetStringSelection(desc);
|
|
} else if (mOutput->GetStringSelection() != desc &&
|
|
mOutput->GetCount()) {
|
|
for (size_t i = 0; i < outMaps.size(); i++) {
|
|
if (outMaps[i].hostString == hostName &&
|
|
MakeDeviceSourceString(&outMaps[i]) == mOutput->GetString(0)) {
|
|
// use the default. It should exist but check just in case, falling back on the 0 index.
|
|
DeviceSourceMap *defaultMap = DeviceManager::Instance()->GetDefaultOutputDevice(outMaps[i].hostIndex);
|
|
if (defaultMap) {
|
|
mOutput->SetStringSelection(MakeDeviceSourceString(defaultMap));
|
|
SetDevices(NULL, defaultMap);
|
|
} else {
|
|
//use the first item (0th index) if we have no familiar devices
|
|
mOutput->SetSelection(0);
|
|
SetDevices(NULL, &outMaps[i]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
long oldChannels;
|
|
oldChannels = mInputChannels->GetSelection() + 1;
|
|
auto newChannels = AudioIORecordChannels.ReadWithDefault(0);
|
|
if (newChannels > 0 && oldChannels != newChannels)
|
|
mInputChannels->SetSelection(newChannels - 1);
|
|
|
|
if (!hostName.empty() && mHost->GetStringSelection() != hostName)
|
|
mHost->SetStringSelection(hostName);
|
|
|
|
RegenerateTooltips();
|
|
|
|
// Set label to pull in language change
|
|
SetLabel(XO("Device"));
|
|
|
|
// Give base class a chance
|
|
ToolBar::UpdatePrefs();
|
|
|
|
Layout();
|
|
Refresh();
|
|
}
|
|
|
|
void DeviceToolBar::UpdateSelectedPrefs( int id )
|
|
{
|
|
if (id == DeviceToolbarPrefsID())
|
|
UpdatePrefs();
|
|
ToolBar::UpdateSelectedPrefs( id );
|
|
}
|
|
|
|
|
|
void DeviceToolBar::EnableDisableButtons()
|
|
{
|
|
auto gAudioIO = AudioIOBase::Get();
|
|
if (gAudioIO) {
|
|
// we allow changes when monitoring, but not when recording
|
|
bool audioStreamActive = gAudioIO->IsStreamActive() && !gAudioIO->IsMonitoring();
|
|
|
|
// Here we should relinquish focus
|
|
if (audioStreamActive) {
|
|
wxWindow *focus = wxWindow::FindFocus();
|
|
if (focus == mHost || focus == mInput || focus == mOutput || focus == mInputChannels)
|
|
TrackPanel::Get( mProject ).SetFocus();
|
|
}
|
|
|
|
mHost->Enable(!audioStreamActive);
|
|
mInput->Enable(!audioStreamActive);
|
|
mOutput->Enable(!audioStreamActive);
|
|
mInputChannels->Enable(!audioStreamActive);
|
|
}
|
|
}
|
|
|
|
void DeviceToolBar::SetNames()
|
|
{
|
|
/* i18n-hint: (noun) It's the device used for playback.*/
|
|
mOutput->SetName(_("Playback Device"));
|
|
/* i18n-hint: (noun) It's the device used for recording.*/
|
|
mInput->SetName(_("Recording Device"));
|
|
mHost->SetName(_("Audio Host"));
|
|
mInputChannels->SetName(_("Recording Channels"));
|
|
}
|
|
|
|
void DeviceToolBar::RegenerateTooltips()
|
|
{
|
|
#if wxUSE_TOOLTIPS
|
|
SetNames();
|
|
mOutput->SetToolTip(mOutput->GetName() + wxT(" - ") + mOutput->GetStringSelection());
|
|
mInput->SetToolTip(mInput->GetName() + wxT(" - ") + mInput->GetStringSelection());
|
|
mHost->SetToolTip(mHost->GetName() + wxT(" - ") + mHost->GetStringSelection());
|
|
mInputChannels->SetToolTip(mInputChannels->GetName() + wxT(" - ") + mInputChannels->GetStringSelection());
|
|
#endif
|
|
}
|
|
|
|
void DeviceToolBar::RefillCombos()
|
|
{
|
|
FillHosts();
|
|
FillHostDevices();
|
|
FillInputChannels();
|
|
// make the device display selection reflect the prefs if they exist
|
|
UpdatePrefs();
|
|
}
|
|
|
|
void DeviceToolBar::FillHosts()
|
|
{
|
|
const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
|
|
const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
|
|
|
|
wxArrayString hosts;
|
|
|
|
// go over our lists add the host to the list if it isn't there yet
|
|
|
|
for (auto & device : inMaps) {
|
|
if (!make_iterator_range(hosts).contains(device.hostString)) {
|
|
hosts.push_back(device.hostString);
|
|
}
|
|
}
|
|
|
|
for (auto & device : outMaps) {
|
|
if (!make_iterator_range(hosts).contains(device.hostString)) {
|
|
hosts.push_back(device.hostString);
|
|
}
|
|
}
|
|
|
|
mHost->Clear();
|
|
mHost->Append(hosts);
|
|
|
|
if (hosts.size() == 0) {
|
|
mHost->Enable(false);
|
|
}
|
|
|
|
mHost->SetMinSize(wxSize(50, wxDefaultCoord));
|
|
}
|
|
|
|
void DeviceToolBar::FillHostDevices()
|
|
{
|
|
const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
|
|
const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
|
|
|
|
//read what is in the prefs
|
|
auto host = AudioIOHost.Read();
|
|
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 (auto & device : outMaps) {
|
|
if (device.hostString == host) {
|
|
foundHostIndex = device.hostIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (foundHostIndex == -1) {
|
|
for (auto & device : inMaps) {
|
|
if (device.hostString == host) {
|
|
foundHostIndex = device.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;
|
|
}
|
|
}
|
|
|
|
// Make sure in/out are clear in case no host was found
|
|
mInput->Clear();
|
|
mOutput->Clear();
|
|
|
|
// 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
|
|
for (auto & device : inMaps) {
|
|
if (foundHostIndex == device.hostIndex) {
|
|
mInput->Append(MakeDeviceSourceString(&device));
|
|
if (host.empty()) {
|
|
host = device.hostString;
|
|
AudioIOHost.Write(host);
|
|
mHost->SetStringSelection(host);
|
|
}
|
|
}
|
|
}
|
|
mInput->Enable(mInput->GetCount() ? true : false);
|
|
|
|
mInput->SetMinSize(wxSize(50, wxDefaultCoord));
|
|
|
|
for (auto & device : outMaps) {
|
|
if (foundHostIndex == device.hostIndex) {
|
|
mOutput->Append(MakeDeviceSourceString(&device));
|
|
if (host.empty()) {
|
|
host = device.hostString;
|
|
AudioIOHost.Write(host);
|
|
gPrefs->Flush();
|
|
mHost->SetStringSelection(host);
|
|
}
|
|
}
|
|
}
|
|
mOutput->Enable(mOutput->GetCount() ? true : false);
|
|
|
|
mOutput->SetMinSize(wxSize(50, wxDefaultCoord));
|
|
|
|
// The setting of the Device is left up to OnChoice
|
|
}
|
|
|
|
void DeviceToolBar::FillInputChannels()
|
|
{
|
|
const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
|
|
auto host = AudioIOHost.Read();
|
|
auto device = AudioIORecordingDevice.Read();
|
|
auto source = AudioIORecordingSource.Read();
|
|
long newChannels;
|
|
|
|
auto oldChannels = AudioIORecordChannels.Read();
|
|
mInputChannels->Clear();
|
|
for (auto & dev: inMaps) {
|
|
if (source == dev.sourceString &&
|
|
device == dev.deviceString &&
|
|
host == dev.hostString) {
|
|
|
|
// add one selection for each channel of this source
|
|
for (size_t j = 0; j < (unsigned int) dev.numChannels; j++) {
|
|
wxString name;
|
|
|
|
if (j == 0) {
|
|
name = _("1 (Mono) Recording Channel");
|
|
}
|
|
else if (j == 1) {
|
|
name = _("2 (Stereo) Recording Channels");
|
|
}
|
|
else {
|
|
name = wxString::Format(wxT("%d"), (int) j + 1);
|
|
}
|
|
mInputChannels->Append(name);
|
|
}
|
|
newChannels = dev.numChannels;
|
|
if (oldChannels <= newChannels && oldChannels >= 1) {
|
|
newChannels = oldChannels;
|
|
}
|
|
if (newChannels >= 1) {
|
|
mInputChannels->SetSelection(newChannels - 1);
|
|
}
|
|
AudioIORecordChannels.Write(newChannels);
|
|
break;
|
|
}
|
|
}
|
|
mInputChannels->Enable(mInputChannels->GetCount() ? true : false);
|
|
|
|
mInputChannels->SetMinSize(wxSize(50, wxDefaultCoord));
|
|
}
|
|
|
|
void DeviceToolBar::OnRescannedDevices( wxCommandEvent &event )
|
|
{
|
|
event.Skip();
|
|
// Hosts may have disappeared or appeared so a complete repopulate is needed.
|
|
RefillCombos();
|
|
}
|
|
|
|
//return 1 if host changed, 0 otherwise.
|
|
int DeviceToolBar::ChangeHost()
|
|
{
|
|
int hostSelectionIndex;
|
|
hostSelectionIndex = mHost->GetSelection();
|
|
|
|
auto oldHost = AudioIOHost.Read();
|
|
wxString newHost = hostSelectionIndex >= 0 ? mHost->GetString(hostSelectionIndex) :
|
|
oldHost;
|
|
|
|
if (oldHost == newHost)
|
|
return 0;
|
|
|
|
//change the host and switch to correct devices.
|
|
AudioIOHost.Write(newHost);
|
|
gPrefs->Flush();
|
|
|
|
// populate the devices
|
|
FillHostDevices();
|
|
|
|
return 1;
|
|
}
|
|
|
|
void DeviceToolBar::SetDevices(const DeviceSourceMap *in, const DeviceSourceMap *out)
|
|
{
|
|
if (in) {
|
|
AudioIORecordingDevice.Write(in->deviceString);
|
|
AudioIORecordingSourceIndex.Write(in->sourceIndex);
|
|
if (in->totalSources >= 1)
|
|
AudioIORecordingSource.Write(in->sourceString);
|
|
else
|
|
AudioIORecordingSource.Reset();
|
|
gPrefs->Flush();
|
|
|
|
FillInputChannels();
|
|
}
|
|
|
|
if (out) {
|
|
AudioIOPlaybackDevice.Write(out->deviceString);
|
|
if (out->totalSources >= 1) {
|
|
gPrefs->Write(wxT("/AudioIO/PlaybackSource"), out->sourceString);
|
|
} else {
|
|
gPrefs->Write(wxT("/AudioIO/PlaybackSource"), wxT(""));
|
|
}
|
|
gPrefs->Flush();
|
|
}
|
|
}
|
|
|
|
void DeviceToolBar::ChangeDevice(bool isInput)
|
|
{
|
|
int newIndex = -1;
|
|
wxChoice *combo = isInput ? mInput :mOutput;
|
|
size_t i;
|
|
|
|
int selectionIndex = combo->GetSelection();
|
|
auto host = AudioIOHost.Read();
|
|
const std::vector<DeviceSourceMap> &maps = isInput ? DeviceManager::Instance()->GetInputDeviceMaps()
|
|
: DeviceManager::Instance()->GetOutputDeviceMaps();
|
|
|
|
// 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)
|
|
AudioIORecordChannels.Write(channelsSelectionIndex + 1);
|
|
} else if (eventObject == mInput) {
|
|
ChangeDevice(true);
|
|
}
|
|
else if (eventObject == mOutput) {
|
|
ChangeDevice(false);
|
|
}
|
|
|
|
auto gAudioIO = AudioIOBase::Get();
|
|
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.
|
|
// TODO: 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();
|
|
}
|
|
|
|
wxTheApp->AddPendingEvent(wxCommandEvent{
|
|
EVT_PREFS_UPDATE, DeviceToolbarPrefsID() });
|
|
}
|
|
|
|
void DeviceToolBar::ShowInputDialog()
|
|
{
|
|
ShowComboDialog(mInput, XO("Select Recording Device"));
|
|
}
|
|
void DeviceToolBar::ShowOutputDialog()
|
|
{
|
|
ShowComboDialog(mOutput, XO("Select Playback Device"));
|
|
}
|
|
void DeviceToolBar::ShowHostDialog()
|
|
{
|
|
ShowComboDialog(mHost, XO("Select Audio Host"));
|
|
}
|
|
void DeviceToolBar::ShowChannelsDialog()
|
|
{
|
|
ShowComboDialog(mInputChannels, XO("Select Recording Channels"));
|
|
}
|
|
|
|
void DeviceToolBar::ShowComboDialog(wxChoice *combo, const TranslatableString &title)
|
|
{
|
|
if (!combo || combo->GetCount() == 0) {
|
|
AudacityMessageBox( XO("Device information is not available.") );
|
|
return;
|
|
}
|
|
|
|
#if USE_PORTMIXER
|
|
wxArrayStringEx inputSources = combo->GetStrings();
|
|
|
|
wxDialogWrapper dlg(nullptr, wxID_ANY, title);
|
|
dlg.SetName();
|
|
ShuttleGui S(&dlg, eIsCreating);
|
|
wxChoice *c;
|
|
|
|
S.StartVerticalLay(true);
|
|
{
|
|
S.StartHorizontalLay(wxCENTER, false);
|
|
{
|
|
c = S.AddChoice( Verbatim( combo->GetName() ),
|
|
transform_container<TranslatableStrings>( inputSources, Verbatim ),
|
|
combo->GetSelection());
|
|
c->SetMinSize(c->GetBestSize());
|
|
}
|
|
S.EndHorizontalLay();
|
|
}
|
|
S.EndVerticalLay();
|
|
S.AddStandardButtons();
|
|
|
|
dlg.GetSizer()->SetSizeHints(&dlg);
|
|
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
|
|
}
|
|
|
|
static RegisteredToolbarFactory factory{ DeviceBarID,
|
|
[]( AudacityProject &project ){
|
|
return ToolBar::Holder{ safenew DeviceToolBar{ project } }; }
|
|
};
|
|
|
|
namespace {
|
|
AttachedToolBarMenuItem sAttachment{
|
|
/* i18n-hint: Clicking this menu item shows the toolbar
|
|
that manages devices */
|
|
DeviceBarID, wxT("ShowDeviceTB"), XXO("&Device Toolbar")
|
|
};
|
|
}
|
|
|