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