Audacity  3.0.3
DeviceManager.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity - A Digital Audio Editor
4  Copyright 1999-2010 Audacity Team
5  Michael Chinen
6 
7 ******************************************************************/
8 
9 
10 #include "DeviceManager.h"
11 
12 #include <wx/log.h>
13 #include <thread>
14 
15 
16 
17 #include "portaudio.h"
18 #ifdef __WXMSW__
19 #include "pa_win_wasapi.h"
20 #endif
21 
22 #ifdef USE_PORTMIXER
23 #include "portmixer.h"
24 #endif
25 
26 #include "AudioIOBase.h"
27 
28 #include "DeviceChange.h" // for HAVE_DEVICE_CHANGE
29 
30 wxDEFINE_EVENT(EVT_RESCANNED_DEVICES, wxEvent);
31 
33 
36 {
37  return &dm;
38 }
39 
40 const std::vector<DeviceSourceMap> &DeviceManager::GetInputDeviceMaps()
41 {
42  if (!m_inited)
43  Init();
45 }
46 const std::vector<DeviceSourceMap> &DeviceManager::GetOutputDeviceMaps()
47 {
48  if (!m_inited)
49  Init();
51 }
52 
53 
55 {
56  wxString ret;
57  ret = map->deviceString;
58  if (map->totalSources > 1)
59  ret += wxT(": ") + map->sourceString;
60 
61  return ret;
62 }
63 
65 {
66  if (hostIndex < 0 || hostIndex >= Pa_GetHostApiCount()) {
67  return NULL;
68  }
69 
70  const struct PaHostApiInfo *apiinfo = Pa_GetHostApiInfo(hostIndex); // get info on API
71  std::vector<DeviceSourceMap> & maps = isInput ? mInputDeviceSourceMaps : mOutputDeviceSourceMaps;
72  size_t i;
73  int targetDevice = isInput ? apiinfo->defaultInputDevice : apiinfo->defaultOutputDevice;
74 
75  for (i = 0; i < maps.size(); i++) {
76  if (maps[i].deviceIndex == targetDevice)
77  return &maps[i];
78  }
79 
80  wxLogDebug(wxT("GetDefaultDevice() no default device"));
81  return NULL;
82 }
83 
85 {
86  return GetDefaultDevice(hostIndex, 0);
87 }
89 {
90  return GetDefaultDevice(hostIndex, 1);
91 }
92 
93 //--------------- Device Enumeration --------------------------
94 
95 //Port Audio requires we open the stream with a callback or a lot of devices will fail
96 //as this means open in blocking mode, so we use a dummy one.
98  const void *WXUNUSED(input), void * WXUNUSED(output),
99  unsigned long WXUNUSED(frameCount),
100  const PaStreamCallbackTimeInfo* WXUNUSED(timeInfo),
101  PaStreamCallbackFlags WXUNUSED(statusFlags),
102  void *WXUNUSED(userData) )
103 {
104  return 0;
105 }
106 
107 static void FillHostDeviceInfo(DeviceSourceMap *map, const PaDeviceInfo *info, int deviceIndex, int isInput)
108 {
109  wxString hostapiName = wxSafeConvertMB2WX(Pa_GetHostApiInfo(info->hostApi)->name);
110  wxString infoName = wxSafeConvertMB2WX(info->name);
111 
112  map->deviceIndex = deviceIndex;
113  map->hostIndex = info->hostApi;
114  map->deviceString = infoName;
115  map->hostString = hostapiName;
116  map->numChannels = isInput ? info->maxInputChannels : info->maxOutputChannels;
117 }
118 
119 static void AddSourcesFromStream(int deviceIndex, const PaDeviceInfo *info, std::vector<DeviceSourceMap> *maps, PaStream *stream)
120 {
121 #ifdef USE_PORTMIXER
122  int i;
123 #endif
124  DeviceSourceMap map;
125 
126  map.sourceIndex = -1;
127  map.totalSources = 0;
128  // Only inputs have sources, so we call FillHostDeviceInfo with a 1 to indicate this
129  FillHostDeviceInfo(&map, info, deviceIndex, 1);
130 
131 #ifdef USE_PORTMIXER
132  PxMixer *portMixer = Px_OpenMixer(stream, deviceIndex, -1, 0);
133  if (!portMixer) {
134  maps->push_back(map);
135  return;
136  }
137 
138  //if there is only one source, we don't need to concatenate the source
139  //or enumerate, because it is something meaningless like 'master'
140  //(as opposed to 'mic in' or 'line in'), and the user doesn't have any choice.
141  //note that some devices have no input sources at all but are still valid.
142  //the behavior we do is the same for 0 and 1 source cases.
143  map.totalSources = Px_GetNumInputSources(portMixer);
144 #endif
145 
146  if (map.totalSources <= 1) {
147  map.sourceIndex = 0;
148  maps->push_back(map);
149  }
150 #ifdef USE_PORTMIXER
151  else {
152  //open up a stream with the device so portmixer can get the info out of it.
153  for (i = 0; i < map.totalSources; i++) {
154  map.sourceIndex = i;
155  map.sourceString = wxString(wxSafeConvertMB2WX(Px_GetInputSourceName(portMixer, i)));
156  maps->push_back(map);
157  }
158  }
159  Px_CloseMixer(portMixer);
160 #endif
161 }
162 
163 static bool IsInputDeviceAMapperDevice(const PaDeviceInfo *info)
164 {
165  // For Windows only, portaudio returns the default mapper object
166  // as the first index after a NEW hostApi index is detected (true for MME and DS)
167  // this is a bit of a hack, but there's no other way to find out which device is a mapper,
168  // I've looked at string comparisons, but if the system is in a different language this breaks.
169 #ifdef __WXMSW__
170  static int lastHostApiTypeId = -1;
171  int hostApiTypeId = Pa_GetHostApiInfo(info->hostApi)->type;
172  if(hostApiTypeId != lastHostApiTypeId &&
173  (hostApiTypeId == paMME || hostApiTypeId == paDirectSound)) {
174  lastHostApiTypeId = hostApiTypeId;
175  return true;
176  }
177 #endif
178 
179  return false;
180 }
181 
182 static void AddSources(int deviceIndex, int rate, std::vector<DeviceSourceMap> *maps, int isInput)
183 {
184  int error = 0;
185  DeviceSourceMap map;
186  const PaDeviceInfo *info = Pa_GetDeviceInfo(deviceIndex);
187 
188  // This tries to open the device with the samplerate worked out above, which
189  // will be the highest available for play and record on the device, or
190  // 44.1kHz if the info cannot be fetched.
191 
192  PaStream *stream = NULL;
193 
194  PaStreamParameters parameters;
195 
196  parameters.device = deviceIndex;
197  parameters.sampleFormat = paFloat32;
198  parameters.hostApiSpecificStreamInfo = NULL;
199  parameters.channelCount = 1;
200 
201  // If the device is for input, open a stream so we can use portmixer to query
202  // the number of inputs. We skip this for outputs because there are no 'sources'
203  // and some platforms (e.g. XP) have the same device for input and output, (while
204  // Vista/Win7 separate these into two devices with the same names (but different
205  // portaudio indices)
206  // Also, for mapper devices we don't want to keep any sources, so check for it here
207  if (isInput && !IsInputDeviceAMapperDevice(info)) {
208  if (info)
209  parameters.suggestedLatency = info->defaultLowInputLatency;
210  else
211  parameters.suggestedLatency = 10.0;
212 
213  error = Pa_OpenStream(&stream,
214  &parameters,
215  NULL,
216  rate, paFramesPerBufferUnspecified,
217  paClipOff | paDitherOff,
218  DummyPaStreamCallback, NULL);
219  }
220 
221  if (stream && !error) {
222  AddSourcesFromStream(deviceIndex, info, maps, stream);
223  Pa_CloseStream(stream);
224  } else {
225  map.sourceIndex = -1;
226  map.totalSources = 0;
227  FillHostDeviceInfo(&map, info, deviceIndex, isInput);
228  maps->push_back(map);
229  }
230 
231  if(error) {
232  wxLogDebug(wxT("PortAudio stream error creating device list: ") +
233  map.hostString + wxT(":") + map.deviceString + wxT(": ") +
234  wxString(wxSafeConvertMB2WX(Pa_GetErrorText((PaError)error))));
235  }
236 }
237 
238 namespace {
239 struct MyEvent : wxEvent {
240  using wxEvent::wxEvent;
241  wxEvent *Clone() const override { return new MyEvent{*this}; }
242 };
243 }
244 
248 {
249  // get rid of the previous scan info
250  this->mInputDeviceSourceMaps.clear();
251  this->mOutputDeviceSourceMaps.clear();
252 
253  // if we are doing a second scan then restart portaudio to get NEW devices
254  if (m_inited) {
255  // check to see if there is a stream open - can happen if monitoring,
256  // but otherwise Rescan() should not be available to the user.
257  auto gAudioIO = AudioIOBase::Get();
258  if (gAudioIO) {
259  if (gAudioIO->IsMonitoring())
260  {
261  using namespace std::chrono;
262  gAudioIO->StopStream();
263  while (gAudioIO->IsBusy())
264  std::this_thread::sleep_for(100ms);
265  }
266  }
267 
268  // restart portaudio - this updates the device list
269  // FIXME: TRAP_ERR restarting PortAudio
270  Pa_Terminate();
271  Pa_Initialize();
272  }
273 
274  // FIXME: TRAP_ERR PaErrorCode not handled in ReScan()
275  int nDevices = Pa_GetDeviceCount();
276 
277  //The hierarchy for devices is Host/device/source.
278  //Some newer systems aggregate this.
279  //So we need to call port mixer for every device to get the sources
280  for (int i = 0; i < nDevices; i++) {
281  const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
282  if (info->maxOutputChannels > 0) {
283  AddSources(i, info->defaultSampleRate, &mOutputDeviceSourceMaps, 0);
284  }
285 
286  if (info->maxInputChannels > 0) {
287 #ifdef __WXMSW__
288 #if !defined(EXPERIMENTAL_FULL_WASAPI)
289  if (Pa_GetHostApiInfo(info->hostApi)->type != paWASAPI ||
290  PaWasapi_IsLoopback(i) > 0)
291 #endif
292 #endif
293  AddSources(i, info->defaultSampleRate, &mInputDeviceSourceMaps, 1);
294  }
295  }
296 
297  // If this was not an initial scan update each device toolbar.
298  if ( m_inited ) {
299  MyEvent e{ 0, EVT_RESCANNED_DEVICES };
300  this->ProcessEvent( e );
301  }
302 
303  m_inited = true;
304  mRescanTime = std::chrono::steady_clock::now();
305 }
306 
307 
309  auto now = std::chrono::steady_clock::now();
310  auto dur = std::chrono::duration_cast<std::chrono::duration<float>>(now - mRescanTime);
311  return dur.count();
312 }
313 
314 
315 //private constructor - Singleton.
317 #if defined(EXPERIMENTAL_DEVICE_CHANGE_HANDLER)
318 #if defined(HAVE_DEVICE_CHANGE)
319 : DeviceChangeHandler()
320 #endif
321 #endif
322 {
323  m_inited = false;
324  mRescanTime = std::chrono::steady_clock::now();
325 }
326 
328 {
329 
330 }
331 
333 {
334  Rescan();
335 
336 #if defined(EXPERIMENTAL_DEVICE_CHANGE_HANDLER)
337 #if defined(HAVE_DEVICE_CHANGE)
338  DeviceChangeHandler::Enable(true);
339 #endif
340 #endif
341 }
342 
343 #if defined(EXPERIMENTAL_DEVICE_CHANGE_HANDLER)
344 #if defined(HAVE_DEVICE_CHANGE)
345 void DeviceManager::DeviceChangeNotification()
346 {
347  Rescan();
348  return;
349 }
350 #endif
351 #endif
DeviceManager::dm
static DeviceManager dm
Definition: DeviceManager.h:98
DeviceManager::Rescan
void Rescan()
Definition: DeviceManager.cpp:247
AudioIOBase.h
anonymous_namespace{DeviceManager.cpp}::MyEvent
Definition: DeviceManager.cpp:239
DeviceManager::mInputDeviceSourceMaps
std::vector< DeviceSourceMap > mInputDeviceSourceMaps
Definition: DeviceManager.h:95
DeviceSourceMap::numChannels
int numChannels
Definition: DeviceManager.h:40
PaStream
void PaStream
Definition: AudioIOBase.h:23
FillHostDeviceInfo
static void FillHostDeviceInfo(DeviceSourceMap *map, const PaDeviceInfo *info, int deviceIndex, int isInput)
Definition: DeviceManager.cpp:107
DeviceManager::m_inited
bool m_inited
Definition: DeviceManager.h:93
MakeDeviceSourceString
wxString MakeDeviceSourceString(const DeviceSourceMap *map)
Definition: DeviceManager.cpp:54
DeviceSourceMap::deviceString
wxString deviceString
Definition: DeviceManager.h:42
IsInputDeviceAMapperDevice
static bool IsInputDeviceAMapperDevice(const PaDeviceInfo *info)
Definition: DeviceManager.cpp:163
DeviceSourceMap
Definition: DeviceManager.h:35
DeviceManager::GetDefaultDevice
DeviceSourceMap * GetDefaultDevice(int hostIndex, int isInput)
Definition: DeviceManager.cpp:64
MyEvent
Definition: Prefs.cpp:77
DeviceManager::GetDefaultOutputDevice
DeviceSourceMap * GetDefaultOutputDevice(int hostIndex)
Definition: DeviceManager.cpp:84
DeviceManager::Init
void Init()
Definition: DeviceManager.cpp:332
PaError
int PaError
Definition: AudioIO.h:53
DeviceManager::mOutputDeviceSourceMaps
std::vector< DeviceSourceMap > mOutputDeviceSourceMaps
Definition: DeviceManager.h:96
DeviceManager::GetTimeSinceRescan
float GetTimeSinceRescan()
Definition: DeviceManager.cpp:308
AudioIOBase::Get
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:89
wxDEFINE_EVENT
wxDEFINE_EVENT(EVT_RESCANNED_DEVICES, wxEvent)
AddSourcesFromStream
static void AddSourcesFromStream(int deviceIndex, const PaDeviceInfo *info, std::vector< DeviceSourceMap > *maps, PaStream *stream)
Definition: DeviceManager.cpp:119
DeviceSourceMap::totalSources
int totalSources
Definition: DeviceManager.h:39
DeviceSourceMap::deviceIndex
int deviceIndex
Definition: DeviceManager.h:36
DeviceSourceMap::sourceIndex
int sourceIndex
Definition: DeviceManager.h:37
DeviceManager.h
DeviceManager::~DeviceManager
~DeviceManager()
Definition: DeviceManager.cpp:327
PaStreamCallbackFlags
unsigned long PaStreamCallbackFlags
Definition: AudioIO.h:51
DeviceManager
A singleton that manages the audio devices known to Audacity.
Definition: DeviceManager.h:55
DeviceManager::GetDefaultInputDevice
DeviceSourceMap * GetDefaultInputDevice(int hostIndex)
Definition: DeviceManager.cpp:88
DeviceManager::Instance
static DeviceManager * Instance()
Gets the singleton instance.
Definition: DeviceManager.cpp:35
DeviceChange.h
DeviceSourceMap::hostString
wxString hostString
Definition: DeviceManager.h:43
DeviceManager::GetInputDeviceMaps
const std::vector< DeviceSourceMap > & GetInputDeviceMaps()
Definition: DeviceManager.cpp:40
DeviceSourceMap::hostIndex
int hostIndex
Definition: DeviceManager.h:38
DeviceSourceMap::sourceString
wxString sourceString
Definition: DeviceManager.h:41
DeviceManager::GetOutputDeviceMaps
const std::vector< DeviceSourceMap > & GetOutputDeviceMaps()
Definition: DeviceManager.cpp:46
DeviceManager::DeviceManager
DeviceManager()
Definition: DeviceManager.cpp:316
anonymous_namespace{DeviceManager.cpp}::MyEvent::Clone
wxEvent * Clone() const override
Definition: DeviceManager.cpp:241
DummyPaStreamCallback
static int DummyPaStreamCallback(const void *WXUNUSED(input), void *WXUNUSED(output), unsigned long WXUNUSED(frameCount), const PaStreamCallbackTimeInfo *WXUNUSED(timeInfo), PaStreamCallbackFlags WXUNUSED(statusFlags), void *WXUNUSED(userData))
Definition: DeviceManager.cpp:97
AddSources
static void AddSources(int deviceIndex, int rate, std::vector< DeviceSourceMap > *maps, int isInput)
Definition: DeviceManager.cpp:182
DeviceManager::mRescanTime
std::chrono::time_point< std::chrono::steady_clock > mRescanTime
Definition: DeviceManager.h:81