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 
32 DeviceManager DeviceManager::dm;
33 
34 /// Gets the singleton instance
Instance()35 DeviceManager* DeviceManager::Instance()
36 {
37    return &dm;
38 }
39 
GetInputDeviceMaps()40 const std::vector<DeviceSourceMap> &DeviceManager::GetInputDeviceMaps()
41 {
42    if (!m_inited)
43       Init();
44    return mInputDeviceSourceMaps;
45 }
GetOutputDeviceMaps()46 const std::vector<DeviceSourceMap> &DeviceManager::GetOutputDeviceMaps()
47 {
48    if (!m_inited)
49       Init();
50    return mOutputDeviceSourceMaps;
51 }
52 
53 
MakeDeviceSourceString(const DeviceSourceMap * map)54 wxString MakeDeviceSourceString(const DeviceSourceMap *map)
55 {
56    wxString ret;
57    ret = map->deviceString;
58    if (map->totalSources > 1)
59       ret += wxT(": ") + map->sourceString;
60 
61    return ret;
62 }
63 
GetDefaultDevice(int hostIndex,int isInput)64 DeviceSourceMap* DeviceManager::GetDefaultDevice(int hostIndex, int isInput)
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 
GetDefaultOutputDevice(int hostIndex)84 DeviceSourceMap* DeviceManager::GetDefaultOutputDevice(int hostIndex)
85 {
86    return GetDefaultDevice(hostIndex, 0);
87 }
GetDefaultInputDevice(int hostIndex)88 DeviceSourceMap* DeviceManager::GetDefaultInputDevice(int hostIndex)
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.
DummyPaStreamCallback(const void * WXUNUSED (input),void * WXUNUSED (output),unsigned long WXUNUSED (frameCount),const PaStreamCallbackTimeInfo * WXUNUSED (timeInfo),PaStreamCallbackFlags WXUNUSED (statusFlags),void * WXUNUSED (userData))97 static int DummyPaStreamCallback(
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 
FillHostDeviceInfo(DeviceSourceMap * map,const PaDeviceInfo * info,int deviceIndex,int isInput)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 
AddSourcesFromStream(int deviceIndex,const PaDeviceInfo * info,std::vector<DeviceSourceMap> * maps,PaStream * stream)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 
IsInputDeviceAMapperDevice(const PaDeviceInfo * info)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 
AddSources(int deviceIndex,int rate,std::vector<DeviceSourceMap> * maps,int isInput)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;
Clone__anon9a6cae4f0111::MyEvent241    wxEvent *Clone() const override { return new MyEvent{*this}; }
242 };
243 }
244 
245 /// Gets a NEW list of devices by terminating and restarting portaudio
246 /// Assumes that DeviceManager is only used on the main thread.
Rescan()247 void DeviceManager::Rescan()
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 
GetTimeSinceRescan()308 float DeviceManager::GetTimeSinceRescan() {
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.
DeviceManager()316 DeviceManager::DeviceManager()
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 
~DeviceManager()327 DeviceManager::~DeviceManager()
328 {
329 
330 }
331 
Init()332 void DeviceManager::Init()
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)
DeviceChangeNotification()345 void DeviceManager::DeviceChangeNotification()
346 {
347    Rescan();
348    return;
349 }
350 #endif
351 #endif
352