File: WindowsAudioInputDevice_mixer.cpp

package info (click to toggle)
liblivemedia 2012.05.17-1%2Bwheezy1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 4,148 kB
  • sloc: cpp: 44,302; ansic: 944; sh: 89; makefile: 85
file content (496 lines) | stat: -rw-r--r-- 16,162 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// Copyright (c) 2001-2004 Live Networks, Inc.  All rights reserved.
// Windows implementation of a generic audio input device
// This version uses Windows' built-in software mixer.
// Implementation

#include <WindowsAudioInputDevice_mixer.hh>

////////// Mixer and AudioInputPort definition //////////

class AudioInputPort {
public:
  int tag;
  DWORD dwComponentType;
  char name[MIXER_LONG_NAME_CHARS];
};

class Mixer {
public:
  Mixer();
  virtual ~Mixer();

  void open(unsigned numChannels, unsigned samplingFrequency, unsigned granularityInMS);
  void open(); // open with default parameters
  void getPortsInfo();
  Boolean enableInputPort(unsigned portIndex, char const*& errReason, MMRESULT& errCode);
  void close();

  unsigned index;
  HMIXER hMixer; // valid when open
  DWORD dwRecLineID; // valid when open
  unsigned numPorts;
  AudioInputPort* ports;
  char name[MAXPNAMELEN];
};


////////// AudioInputDevice (remaining) implementation //////////

AudioInputDevice*
AudioInputDevice::createNew(UsageEnvironment& env, int inputPortNumber,
			    unsigned char bitsPerSample,
			    unsigned char numChannels,
			    unsigned samplingFrequency,
			    unsigned granularityInMS) {
  Boolean success;
  WindowsAudioInputDevice* newSource
    = new WindowsAudioInputDevice(env, inputPortNumber,
				  bitsPerSample, numChannels,
				  samplingFrequency, granularityInMS,
				  success);
  if (!success) {delete newSource; newSource = NULL;}

  return newSource;
}

AudioPortNames* AudioInputDevice::getPortNames() {
  WindowsAudioInputDevice::initializeIfNecessary();

  AudioPortNames* portNames = new AudioPortNames;
  portNames->numPorts = WindowsAudioInputDevice::numInputPortsTotal;
  portNames->portName = new char*[WindowsAudioInputDevice::numInputPortsTotal];

  // If there's more than one mixer, print only the port name.
  // If there's two or more mixers, also include the mixer name
  // (to disambiguate port names that may be the same name in different mixers)
  char portNameBuffer[2*MAXPNAMELEN+10/*slop*/];
  char mixerNameBuffer[MAXPNAMELEN];
  char const* portNameFmt;
  if (WindowsAudioInputDevice::numMixers <= 1) {
    portNameFmt = "%s";
  } else {
    portNameFmt = "%s (%s)";
  }

  unsigned curPortNum = 0;
  for (unsigned i = 0; i < WindowsAudioInputDevice::numMixers; ++i) {
    Mixer& mixer = WindowsAudioInputDevice::ourMixers[i];

    if (WindowsAudioInputDevice::numMixers <= 1) {
      mixerNameBuffer[0] = '\0';
    } else {
      strncpy(mixerNameBuffer, mixer.name, sizeof mixerNameBuffer);
#if 0
      // Hack: Simplify the mixer name, by truncating after the first space character:
      for (int k = 0; k < sizeof mixerNameBuffer && mixerNameBuffer[k] != '\0'; ++k) {
	if (mixerNameBuffer[k] == ' ') {
	  mixerNameBuffer[k] = '\0';
	  break;
	}
      }
#endif
    }

    for (unsigned j = 0; j < mixer.numPorts; ++j) {
      sprintf(portNameBuffer, portNameFmt, mixer.ports[j].name, mixerNameBuffer);
      portNames->portName[curPortNum++] = strDup(portNameBuffer);
    }
  }

  return portNames;
}


////////// WindowsAudioInputDevice implementation //////////

WindowsAudioInputDevice
::WindowsAudioInputDevice(UsageEnvironment& env, int inputPortNumber,
			  unsigned char bitsPerSample,
			  unsigned char numChannels,
			  unsigned samplingFrequency,
			  unsigned granularityInMS,
			  Boolean& success)
  : WindowsAudioInputDevice_common(env, inputPortNumber,
		bitsPerSample, numChannels, samplingFrequency, granularityInMS),
	fCurMixerId(-1) {
	success = initialSetInputPort(inputPortNumber);
}

WindowsAudioInputDevice::~WindowsAudioInputDevice() {
  if (fCurMixerId >= 0) ourMixers[fCurMixerId].close();

  delete[] ourMixers; ourMixers = NULL;
  numMixers = numInputPortsTotal = 0;
}

void WindowsAudioInputDevice::initializeIfNecessary() {
  if (ourMixers != NULL) return; // we've already been initialized
  numMixers = mixerGetNumDevs();
  ourMixers = new Mixer[numMixers];

  // Initialize each mixer:
  numInputPortsTotal = 0;
  for (unsigned i = 0; i < numMixers; ++i) {
    Mixer& mixer = ourMixers[i];
    mixer.index = i;
    mixer.open();
    if (mixer.hMixer != NULL) {
      // This device has a valid mixer.  Get information about its ports:
      mixer.getPortsInfo();
      mixer.close();

      if (mixer.numPorts == 0) continue;

      numInputPortsTotal += mixer.numPorts;
    } else {
      mixer.ports = NULL;
      mixer.numPorts = 0;
    }
  }
}

Boolean WindowsAudioInputDevice::setInputPort(int portIndex) {
  initializeIfNecessary();

  if (portIndex < 0 || portIndex >= (int)numInputPortsTotal) { // bad index
    envir().setResultMsg("Bad input port index\n");
    return False;
  }

  // Find the mixer and port that corresponds to "portIndex":
  int newMixerId, portWithinMixer, portIndexCount = 0;
  for (newMixerId = 0; newMixerId < (int)numMixers; ++newMixerId) {
    int prevPortIndexCount = portIndexCount;
    portIndexCount += ourMixers[newMixerId].numPorts;
    if (portIndexCount > portIndex) { // it's with this mixer
      portWithinMixer = portIndex - prevPortIndexCount;
      break;
    }
  }

  // Check that this mixer is allowed:
  if (allowedDeviceNames != NULL) {
	  int i;
	  for (i = 0; allowedDeviceNames[i] != NULL; ++i) {
		  if (strncmp(ourMixers[newMixerId].name, allowedDeviceNames[i],
			  strlen(allowedDeviceNames[i])) == 0) {
			  // The allowed device name is a prefix of this mixer's name
			  break; // this mixer is allowed
		  }
	  }
	  if (allowedDeviceNames[i] == NULL) { // this mixer is not on the allowed list
		envir().setResultMsg("Access to this audio device is not allowed\n");
		return False;
	  }
  }

  if (newMixerId != fCurMixerId) {
    // The mixer has changed, so close the old one and open the new one:
    if (fCurMixerId >= 0) ourMixers[fCurMixerId].close();
    fCurMixerId = newMixerId;
    ourMixers[fCurMixerId].open(fNumChannels, fSamplingFrequency, fGranularityInMS);
  }
  if (portIndex != fCurPortIndex) {
    // Change the input port:
    fCurPortIndex = portIndex;
    char const* errReason;
    MMRESULT errCode;
    if (!ourMixers[newMixerId].enableInputPort(portWithinMixer, errReason, errCode)) {
      char resultMsg[100];
      sprintf(resultMsg, "Failed to enable input port: %s failed (0x%08x)\n", errReason, errCode);
      envir().setResultMsg(resultMsg);
      return False;
    }
    // Later, may also need to transfer 'gain' to new port #####
  }
  return True;
}

unsigned WindowsAudioInputDevice::numMixers = 0;

Mixer* WindowsAudioInputDevice::ourMixers = NULL;

unsigned WindowsAudioInputDevice::numInputPortsTotal = 0;


////////// Mixer and AudioInputPort implementation //////////

Mixer::Mixer()
  : hMixer(NULL), dwRecLineID(0), numPorts(0), ports(NULL) {
}

Mixer::~Mixer() {
  delete[] ports;
}

void Mixer::open(unsigned numChannels, unsigned samplingFrequency, unsigned granularityInMS) {
  HMIXER newHMixer = NULL;
  do {
    MIXERCAPS mc;
    if (mixerGetDevCaps(index, &mc, sizeof mc) != MMSYSERR_NOERROR) break;

    #ifdef UNICODE
    // Copy the mixer name:
        wcstombs(name, mc.szPname, MAXPNAMELEN);
    #else
        strncpy(name, mc.szPname, MAXPNAMELEN);
    #endif

    // Find the correct line for this mixer:
    unsigned i, uWavIn;
    unsigned nWavIn = waveInGetNumDevs();
    for (i = 0; i < nWavIn; ++i) {
      WAVEINCAPS wic;
      if (waveInGetDevCaps(i, &wic, sizeof wic) != MMSYSERR_NOERROR) continue;

      MIXERLINE ml;
      ml.cbStruct = sizeof ml;
      ml.Target.dwType  = MIXERLINE_TARGETTYPE_WAVEIN;

    #ifdef UNICODE
          wcsncpy(ml.Target.szPname, wic.szPname, MAXPNAMELEN);
    #else
          strncpy(ml.Target.szPname, wic.szPname, MAXPNAMELEN);
    #endif

      ml.Target.vDriverVersion = wic.vDriverVersion;
      ml.Target.wMid = wic.wMid;
      ml.Target.wPid = wic.wPid;

      if (mixerGetLineInfo((HMIXEROBJ)index, &ml, MIXER_GETLINEINFOF_TARGETTYPE/*|MIXER_OBJECTF_MIXER*/) == MMSYSERR_NOERROR) {
				// this is the right line
	uWavIn = i;
	dwRecLineID = ml.dwLineID;
	break;
      }
    }
    if (i >= nWavIn) break; // error: we couldn't find the right line

    if (mixerOpen(&newHMixer, index, (unsigned long)NULL, (unsigned long)NULL, MIXER_OBJECTF_MIXER) != MMSYSERR_NOERROR) break;
    if (newHMixer == NULL) break;

    // Sanity check: re-call "mixerGetDevCaps()" using the mixer device handle:
    if (mixerGetDevCaps((UINT)newHMixer, &mc, sizeof mc) != MMSYSERR_NOERROR) break;
    if (mc.cDestinations < 1) break; // error: this mixer has no destinations

	if (!WindowsAudioInputDevice_common::openWavInPort(uWavIn, numChannels, samplingFrequency, granularityInMS)) break;

    hMixer = newHMixer;
    return;
  } while (0);

  // An error occurred:
  close();
}

void Mixer::open() {
  open(1, 8000, 20);
}

void Mixer::getPortsInfo() {
  MIXERCAPS mc;
  mixerGetDevCaps((UINT)hMixer, &mc, sizeof mc);

  MIXERLINE mlt;
  unsigned i;
  for (i = 0; i < mc.cDestinations; ++i) {
    memset(&mlt, 0, sizeof mlt);
    mlt.cbStruct = sizeof mlt;
    mlt.dwDestination = i;
    if (mixerGetLineInfo((HMIXEROBJ)hMixer, &mlt, MIXER_GETLINEINFOF_DESTINATION) != MMSYSERR_NOERROR) continue;
    if (mlt.dwLineID == dwRecLineID) break; // this is the destination we're interested in
  }
  ports = new AudioInputPort[mlt.cConnections];

  numPorts = mlt.cConnections;
  for (i = 0; i < numPorts; ++i) {
    MIXERLINE mlc;
    memcpy(&mlc, &mlt, sizeof mlc);
    mlc.dwSource = i;
    mixerGetLineInfo((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINEINFOF_SOURCE/*|MIXER_OBJECTF_HMIXER*/);
    ports[i].tag = mlc.dwLineID;
	ports[i].dwComponentType = mlc.dwComponentType;
#ifdef UNICODE
    wcstombs(ports[i].name, mlc.szName, MIXER_LONG_NAME_CHARS);
#else
    strncpy(ports[i].name, mlc.szName, MIXER_LONG_NAME_CHARS);
#endif
  }

  // Make the microphone the first port in the list:
  for (i = 1; i < numPorts; ++i) {
#ifdef OLD_MICROPHONE_TESTING_CODE
    if (_strnicmp("mic", ports[i].name, 3) == 0 ||
	_strnicmp("mik", ports[i].name, 3) == 0) {
#else
	if (ports[i].dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) {
#endif
      AudioInputPort tmp = ports[0];
      ports[0] = ports[i];
      ports[i] = tmp;
    }
  }
}

Boolean Mixer::enableInputPort(unsigned portIndex, char const*& errReason, MMRESULT& errCode) {
  errReason = NULL; // unless there's an error
  AudioInputPort& port = ports[portIndex];

  MIXERCONTROL mc;
  mc.cMultipleItems = 1; // in case it doesn't get set below
  MIXERLINECONTROLS mlc;
#if 0 // the following doesn't seem to be needed, and can fail:
  mlc.cbStruct = sizeof mlc;
  mlc.pamxctrl = &mc;
  mlc.cbmxctrl = sizeof (MIXERCONTROL);
  mlc.dwLineID = port.tag;
  mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  if ((errCode = mixerGetLineControls((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
    errReason = "mixerGetLineControls()";
    return False;
  }
#endif

  MIXERLINE ml;
  memset(&ml, 0, sizeof (MIXERLINE));
  ml.cbStruct = sizeof (MIXERLINE);
  ml.dwLineID = port.tag;
  if ((errCode = mixerGetLineInfo((HMIXEROBJ)hMixer, &ml, MIXER_GETLINEINFOF_LINEID)) != MMSYSERR_NOERROR) {
    errReason = "mixerGetLineInfo()1";
    return False;
  }

  

  #ifdef UNICODE
    wchar_t portname[MIXER_LONG_NAME_CHARS+1];
    wcsncpy(portname, ml.szName, MIXER_LONG_NAME_CHARS);
  #else
    char portname[MIXER_LONG_NAME_CHARS+1];
    strncpy(portname, ml.szName, MIXER_LONG_NAME_CHARS);
  #endif

  memset(&ml, 0, sizeof (MIXERLINE));
  ml.cbStruct = sizeof (MIXERLINE);
  ml.dwLineID = dwRecLineID;
  if ((errCode = mixerGetLineInfo((HMIXEROBJ)hMixer, &ml, MIXER_GETLINEINFOF_LINEID/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
    errReason = "mixerGetLineInfo()2";
    return False;
  }

  // Get Mixer/MUX control information (need control id to set and get control details)
  mlc.cbStruct = sizeof mlc;
  mlc.dwLineID = ml.dwLineID;
  mlc.cControls = 1;
  mc.cbStruct = sizeof mc; // Needed???#####
  mc.dwControlID = 0xDEADBEEF; // For testing #####
  mlc.pamxctrl = &mc;
  mlc.cbmxctrl = sizeof mc;
  mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX; // Single Select
  if ((errCode = mixerGetLineControls((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
    mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER; // Multiple Select
    mixerGetLineControls((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE/*|MIXER_OBJECTF_HMIXER*/);
  }

  unsigned matchLine = 0;
  if (mc.cMultipleItems > 1) {
    // Before getting control, we need to know which line to grab.
    // We figure this out by listing the lines, and comparing names:
    MIXERCONTROLDETAILS mcd;
    mcd.cbStruct = sizeof mcd;
    mcd.cChannels = ml.cChannels;
    mcd.cMultipleItems = mc.cMultipleItems;
    MIXERCONTROLDETAILS_LISTTEXT* mcdlText = new MIXERCONTROLDETAILS_LISTTEXT[mc.cMultipleItems];
    mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_LISTTEXT);
    mcd.paDetails = mcdlText;

    if (mc.dwControlID != 0xDEADBEEF) { // we know the control id for real
      mcd.dwControlID = mc.dwControlID;
      if ((errCode = mixerGetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_GETCONTROLDETAILSF_LISTTEXT/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
	delete[] mcdlText;
	errReason = "mixerGetControlDetails()1";
	return False;
      }
    } else {
      // Hack: We couldn't find a MUX or MIXER control, so try to guess the control id:
      for (mc.dwControlID = 0; mc.dwControlID < 32; ++mc.dwControlID) {
	mcd.dwControlID = mc.dwControlID;
	if ((errCode = mixerGetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_GETCONTROLDETAILSF_LISTTEXT/*|MIXER_OBJECTF_HMIXER*/)) == MMSYSERR_NOERROR) break;
      }
      if (mc.dwControlID == 32) { // unable to guess mux/mixer control id
	delete[] mcdlText;
	errReason = "mixerGetControlDetails()2";
	return False;
      }
    }

    #ifdef UNICODE
    for (unsigned i = 0; i < mcd.cMultipleItems; ++i) {
        if (wcscmp(mcdlText[i].szName, portname) == 0) {
	    matchLine = i;
	    break;
        }
    }
    #else
    for (unsigned i = 0; i < mcd.cMultipleItems; ++i) {
        if (strcmp(mcdlText[i].szName, portname) == 0) {
	    matchLine = i;
	    break;
        }
    }
    #endif
    
    delete[] mcdlText;
  }

  // Now get control itself:
  MIXERCONTROLDETAILS mcd;
  mcd.cbStruct = sizeof mcd;
  mcd.dwControlID = mc.dwControlID;
  mcd.cChannels = ml.cChannels;
  mcd.cMultipleItems = mc.cMultipleItems;
  MIXERCONTROLDETAILS_BOOLEAN* mcdbState = new MIXERCONTROLDETAILS_BOOLEAN[mc.cMultipleItems];
  mcd.paDetails = mcdbState;
  mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_BOOLEAN);

  if ((errCode = mixerGetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_GETCONTROLDETAILSF_VALUE/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
    delete[] mcdbState;
    errReason = "mixerGetControlDetails()3";
    return False;
  }

  for (unsigned j = 0; j < mcd.cMultipleItems; ++j) {
    mcdbState[j].fValue = (j == matchLine);
  }

  if ((errCode = mixerSetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_OBJECTF_HMIXER)) != MMSYSERR_NOERROR) {
    delete[] mcdbState;
    errReason = "mixerSetControlDetails()";
    return False;
  }
  delete[] mcdbState;

  return True;
}


void Mixer::close() {
  WindowsAudioInputDevice_common::waveIn_close();
  if (hMixer != NULL) mixerClose(hMixer);
  hMixer = NULL; dwRecLineID = 0;
}