AudioDevice.cpp 10.3 KB
Newer Older
jcaceres's avatar
jcaceres committed
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
/*
  JackTrip: A Multimachine System for High-Quality Audio 
  Network Performance over the Internet

  Copyright (c) 2008 Chris Chafe, Juan-Pablo Caceres,
  SoundWIRE group at CCRMA.
  
  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:
  
  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
*/
jcaceres's avatar
jcaceres committed
29

jcaceres's avatar
jcaceres committed
30
31
32
/*
 * audioDevice.cpp
 */
jcaceres's avatar
jcaceres committed
33

jcaceres's avatar
jcaceres committed
34
35
#include "AudioDevice.h"
#include <ctime>
jcaceres's avatar
jcaceres committed
36
37
38
39
using namespace std;

AudioDevice::AudioDevice (bool testMode)
{
jcaceres's avatar
jcaceres committed
40
41
42
43
44
45
46
  if (testMode == false)
    {
      cerr << "You shouldn't ever call new AudioDevice( false );" <<
	endl;
      exit (1);
    }
  /*
jcaceres's avatar
jcaceres committed
47
    else
jcaceres's avatar
jcaceres committed
48
    {
jcaceres's avatar
jcaceres committed
49
    probeAudioDevices ();
jcaceres's avatar
jcaceres committed
50
51
    }
  */
jcaceres's avatar
jcaceres committed
52
53
54
55
}

AudioDevice::AudioDevice (int ID, int numBuffers, audioDeviceModeT mode,
			  AudioInfoT audioInfo):
jcaceres's avatar
jcaceres committed
56
57
58
59
  audioDeviceID (ID),
  audioInfo (audioInfo),
  numBuffers (numBuffers),
  mode (mode)
jcaceres's avatar
jcaceres committed
60
{
jcaceres's avatar
jcaceres committed
61
  streamID = 0;
62
  //int tmpBufferSize = audioInfo->getFramesPerBuffer ();
jcaceres's avatar
jcaceres committed
63
64
65
66
67
68
69
70
71
72
73
74
75
  jack = audioInfo->isJack ();
  if (audioInfo->getNumNetHarpStrings () > 0)
    {
      harp = true;
    }
  else
    {
      harp = false;
    }

  if (jack)
    cout << "Creating Jack audio Device.........";
  /* //Remove STK Dependency
jcaceres's avatar
jcaceres committed
76
77
     else
     cout << "Creating RtAudio Device.........";
jcaceres's avatar
jcaceres committed
78
79
80
  */
  //try
  //{
jcaceres's avatar
jcaceres committed
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
  switch (mode)
    {
    case RECORD:
      if (jack)
	jdevice =
	  new JackClient (//"jacktrip",//APP_NAME, //*JPC Hack, need to fix later
			  audioInfo->
			  getNumChans (),
			  audioInfo->
			  getFramesPerBuffer (),
			  false, true, this, 
			  audioInfo->getJack_alsa_readable_offset ());
      /*//Remove STK Dependency
	else
	device = new RtAudio (streamID,
	0,
	ID,
	audioInfo->
	getNumAudioChans (),
	RTAUDIO_SINT16,
	audioInfo->
	getSampleRate (),
	&(tmpBufferSize),
	numBuffers,
	RtAudio::LINUX_ALSA);
      */

      break;
    case PLAYBACK:
      if (jack)
	jdevice =
	  new JackClient (//"jacktrip",//APP_NAME, //*JPC Hack, need to fix later
			  audioInfo->
			  getNumChans (),
			  audioInfo->
			  getFramesPerBuffer (),
			  true, false, this, 
			  audioInfo->getJack_alsa_readable_offset ());
      /*//Remove STK Dependency
	else
	device = new RtAudio (streamID,
	audioInfo->
	getNumAudioChans (), ID,
	0, RTAUDIO_SINT16,
	audioInfo->
	getSampleRate (),
	&(tmpBufferSize),
	numBuffers,
	RtAudio::LINUX_ALSA);
      */
      break;
    case DUPLEX:
      if (jack)
	jdevice =
	  new JackClient (//"jacktrip",//APP_NAME, //*JPC Hack, need to fix later
			  audioInfo->
			  getNumChans (),
			  audioInfo->
			  getFramesPerBuffer (),
			  true, true, this, 
			  audioInfo->getJack_alsa_readable_offset ());
      /*//Remove STK Dependency
	else
	device = new RtAudio ((streamID),
	audioInfo->
	getNumAudioChans (), ID,
	audioInfo->
	getNumAudioChans (),
	RTAUDIO_SINT16,
	audioInfo->
	getSampleRate (),
	&(tmpBufferSize),
	numBuffers,
	RtAudio::LINUX_ALSA);
      */
      break;
    }
  //}
jcaceres's avatar
jcaceres committed
159
  /*//Remove STK Dependency
jcaceres's avatar
jcaceres committed
160
    catch (RtError (&m))
jcaceres's avatar
jcaceres committed
161
    {
jcaceres's avatar
jcaceres committed
162
163
164
165
    cout << "FAILED!" << endl;
    m.printMessage ();
    probeAudioDevices ();
    exit (1);
jcaceres's avatar
jcaceres committed
166
167
168
169
170
    }
  */
  cout << "SUCCESS!" << endl;

  /*//Remove STK Dependency
jcaceres's avatar
jcaceres committed
171
    if (tmpBufferSize != audioInfo->getFramesPerBuffer ())
jcaceres's avatar
jcaceres committed
172
    {
jcaceres's avatar
jcaceres committed
173
174
    cerr << "RtAudio device could not use requested buffer length of " << audioInfo->getFramesPerBuffer () << " frames.  Using " << tmpBufferSize << endl;
    audioInfo->setFramesPerBuffer (tmpBufferSize);
jcaceres's avatar
jcaceres committed
175
176
177
178
    }
  */

  /*//Remove STK Dependency
jcaceres's avatar
jcaceres committed
179
    if (!jack)
jcaceres's avatar
jcaceres committed
180
    {
jcaceres's avatar
jcaceres committed
181
182
183
184
185
186
    if (device == NULL)
    {
    cerr << "RtAudio Device is NULL!" << endl;
    probeAudioDevices ();
    }
    buffer = device->getStreamBuffer ();
jcaceres's avatar
jcaceres committed
187
188


jcaceres's avatar
jcaceres committed
189
190
191
192
193
    if (buffer == NULL)
    {
    cerr << "RtAudio device buffer is NULL!" << endl;
    probeAudioDevices ();
    }
jcaceres's avatar
jcaceres committed
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
    }
  */

  bytesPerBuffer = audioInfo->getBytesPerBuffer ();

  if (harp == true)
    {
      generateHarpMixMap ();
    }
  else
    {
      readLock = new QSemaphore (1);
      writeLock = new QSemaphore (1);
      //(*writeLock)++;	// lock out write, to start with read
      (*writeLock).acquire(); //****JPC qt4 porting******
    }

  if (jack)
    {
    }
  //else //Remove STK Dependency
  // device->startStream ();
jcaceres's avatar
jcaceres committed
216
217
218
219
220
221
222
223
224
}

AudioDevice::~AudioDevice ()
{
}

void
AudioDevice::jackStart ()
{
jcaceres's avatar
jcaceres committed
225
226
  cout << "jack start" << endl;
  jdevice->start ();
jcaceres's avatar
jcaceres committed
227
228
229
230
231
}

void
AudioDevice::jackStop ()
{
jcaceres's avatar
jcaceres committed
232
233
  cout << "jack stop" << endl;
  jdevice->stop ();
jcaceres's avatar
jcaceres committed
234
235
}

jcaceres's avatar
jcaceres committed
236
/*
jcaceres's avatar
jcaceres committed
237
238
239
  void
  AudioDevice::tick ()
  {
jcaceres's avatar
jcaceres committed
240
  try
jcaceres's avatar
jcaceres committed
241
242
243
244
245
246
247
248
249
250
251
252
253
  {
  device->tickStream (); // blocks until transfer completed
  if (audioInfo->lastTickTime)
  {
  clock_t newTime = clock ();
  fprintf (stderr, "Audio Tick,%d,%d,%.5f\n", newTime,
  *(audioInfo->lastTickTime),
  (newTime -
  *(audioInfo->lastTickTime)) *
  CLOCKS_PER_SEC);
  *(audioInfo->lastTickTime) = newTime;
  }
  }
jcaceres's avatar
jcaceres committed
254
  catch (RtError (&m))
jcaceres's avatar
jcaceres committed
255
256
257
258
  {
  m.printMessage ();
  }
  }
jcaceres's avatar
jcaceres committed
259
*/
jcaceres's avatar
jcaceres committed
260
261
262
263

void
AudioDevice::jtick () // called by jack process after transfer completed
{
jcaceres's avatar
jcaceres committed
264
265
  //	jackWait.wakeAll ();
  //      cout << "jtick" << endl;
jcaceres's avatar
jcaceres committed
266
267
}

268
/*
jcaceres's avatar
jcaceres committed
269
270
271
  void
  AudioDevice::bufferPtrs (void *jib, void *job)
  {
jcaceres's avatar
jcaceres committed
272
273
  //	jibuffer = jib;
  //	jobuffer = job;
jcaceres's avatar
jcaceres committed
274
  }
275
*/
jcaceres's avatar
jcaceres committed
276
277
278
279

void
AudioDevice::unlockRead ()
{
jcaceres's avatar
jcaceres committed
280
  //(*readLock)--;		// so audio input thread will unblock when stopped
jcaceres's avatar
jcaceres committed
281
  (*readLock).release();//****JPC qt4 porting******
jcaceres's avatar
jcaceres committed
282
283
284
285
286
287
}

void
AudioDevice::readBuffer (void *to)
{

jcaceres's avatar
jcaceres committed
288
289
290
291
  if (mode == PLAYBACK)
    {
      cerr << "ERROR: AudioDevice::readBuffer called on device in PLAYBACK mode!" << endl;
    }
jcaceres's avatar
jcaceres committed
292
	
jcaceres's avatar
jcaceres committed
293
294
295
296
297
  //(*readLock)++;
  (*readLock).acquire();//****JPC qt4 porting******
  memcpy (to, buffer, bytesPerBuffer);
  //(*writeLock)--;
  (*writeLock).release();//****JPC qt4 porting******
jcaceres's avatar
jcaceres committed
298
299
300
301
302
303
	
}

void
AudioDevice::writeBuffer (void *from)
{
jcaceres's avatar
jcaceres committed
304
305
306
307
308
309
  if (mode == RECORD)
    {
      cerr << "ERROR: AudioDevice::writeBuffer called on device in RECORD mode!" << endl;
    }
  if (harp == false)
    {
jcaceres's avatar
jcaceres committed
310
		
jcaceres's avatar
jcaceres committed
311
312
313
314
315
316
      //(*writeLock)++;
      (*writeLock).acquire();//****JPC qt4 porting******
      memcpy (buffer, from, bytesPerBuffer);
      //tick (); // calls tickstream, blocks
      //(*readLock)--;
      (*readLock).release();//****JPC qt4 porting******
jcaceres's avatar
jcaceres committed
317
		
jcaceres's avatar
jcaceres committed
318
319
320
    }
  else
    {
jcaceres's avatar
jcaceres committed
321
		
jcaceres's avatar
jcaceres committed
322
323
      harpTick (buffer, from);
      //tick ();
jcaceres's avatar
jcaceres committed
324
		
jcaceres's avatar
jcaceres committed
325
    }
jcaceres's avatar
jcaceres committed
326
327
328
329
330
}

void
AudioDevice::harpTick (void *toBuffer, void *fromBuffer)
{
jcaceres's avatar
jcaceres committed
331
332
333
334
335
336
337
338
339
  double nextSample;
  int stringIndex = 0;
  int sndChans = audioInfo->getNumAudioChans ();
  int netChans = audioInfo->getNumNetHarpStrings ();
  for (int frameIndex = 0;
       frameIndex < audioInfo->getFramesPerBuffer (); frameIndex++)
    {
      stringIndex = 0;
      for (int chanIndex = 0; chanIndex < sndChans; chanIndex++)
jcaceres's avatar
jcaceres committed
340
	{
jcaceres's avatar
jcaceres committed
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
	  nextSample = 0.0;
	  for (int sampleIndex = 0;
	       sampleIndex < stringsPerChan[chanIndex];
	       sampleIndex++)
	    {
	      nextSample +=
		(double) (*
			  ((signed short
			    *) (fromBuffer) +
			   frameIndex *
			   netChans +
			   stringIndex++)) / 32768.0;
	    }
	  *((signed short *) (toBuffer) +
	    frameIndex * sndChans + chanIndex) =
	    (signed short) floor (nextSample * 32768.0);
jcaceres's avatar
jcaceres committed
357
	}
jcaceres's avatar
jcaceres committed
358
    }
jcaceres's avatar
jcaceres committed
359
360
361
362
363
364
365
366
367
}

/**
 * @brief Distribute the NetHarp channels across the audio channels.
 */

void
AudioDevice::generateHarpMixMap ()
{
jcaceres's avatar
jcaceres committed
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
  cerr << "Generating Mixmap for " << audioInfo->getNumNetHarpStrings ()
       << " NetHarp strings to " << audioInfo->getNumAudioChans ()
       << " audio channels." << endl;

  stringsPerChan = new int[audioInfo->getNumAudioChans ()];
  // Set all to 0 initially
  for (int i = 0; i < audioInfo->getNumAudioChans (); i++)
    {
      stringsPerChan[i] = 0;
    }
  // Distribute strings among audio channels
  int stringsAllocated (0);
  int i (0);
  while (stringsAllocated != audioInfo->getNumNetHarpStrings ())
    {
      stringsPerChan[i++]++;
      stringsAllocated++;
      if (i == audioInfo->getNumAudioChans ())
	i = 0;
    }
jcaceres's avatar
jcaceres committed
388
389
}

jcaceres's avatar
jcaceres committed
390

jcaceres's avatar
jcaceres committed
391
392
393
/**
 * @brief Probe audio device to see if it supports required functionality.
 */
jcaceres's avatar
jcaceres committed
394
/*//Remove STK Dependency
jcaceres's avatar
jcaceres committed
395
396
397
  void
  AudioDevice::probeAudioDevices ()
  {
jcaceres's avatar
jcaceres committed
398
  cout << "=====================================================" <<
jcaceres's avatar
jcaceres committed
399
  endl;
jcaceres's avatar
jcaceres committed
400
401
402
403
  cout << " Probing audio devices..." << endl;
  cout << " ------------------------" << endl << endl;

  try
jcaceres's avatar
jcaceres committed
404
405
406
407
408
409
410
411
  {
  device = new RtAudio ();
  } catch (RtError (&m))
  {
  cerr << "AudioDevice::probeAudioDevice() failed!" << endl;
  m.printMessage ();
  exit (1);
  }
jcaceres's avatar
jcaceres committed
412
413
414
415
416
  int numDevices = device->getDeviceCount ();

  RtAudioDeviceInfo info;

  cout << " " << numDevices << " audio devices found.  Probing..." <<
jcaceres's avatar
jcaceres committed
417
  endl << endl;
jcaceres's avatar
jcaceres committed
418
419

  for (int i = 0; i < numDevices; i++)
jcaceres's avatar
jcaceres committed
420
421
422
423
424
425
426
427
428
429
430
431
  {
  try
  {
  info = device->getDeviceInfo (i + 1);
  checkRequirements (i + 1, &info);
  }
  catch (RtError & error)
  {
  error.printMessage ();
  break;
  }
  }
jcaceres's avatar
jcaceres committed
432
433
434
435
  cout << "Check these values against the streambd default command line settings." << endl;
  cout << "Change the relevant parameters as required.  If duplex is not supported" << endl;
  cout << "you will only be able to run in SNDMIRROR mode." << endl;
  cout << "=====================================================" <<
jcaceres's avatar
jcaceres committed
436
437
  endl;
  }
jcaceres's avatar
jcaceres committed
438
*/
jcaceres's avatar
jcaceres committed
439

jcaceres's avatar
jcaceres committed
440
/*//Remove STK Dependency
jcaceres's avatar
jcaceres committed
441
442
443
  void
  AudioDevice::checkRequirements (int deviceNum, RtAudioDeviceInfo * info)
  {
jcaceres's avatar
jcaceres committed
444
445
446
447
448
449
450
  cout << "Device " << deviceNum << ": " << info->name << endl;
  cout << "maxOutputChannels =\t" << info->outputChannels << endl;
  cout << "maxInputChannels =\t" << info->inputChannels << endl;
  cout << "maxDuplexChannels =\t" << info->duplexChannels << endl;

  cout << "hasDuplexSupport =\t";
  if (info->duplexChannels > 0)
jcaceres's avatar
jcaceres committed
451
  cout << "true" << endl;
jcaceres's avatar
jcaceres committed
452
  else
jcaceres's avatar
jcaceres committed
453
  cout << "false." << endl;
jcaceres's avatar
jcaceres committed
454
455
  cout << "Sample rates supported: " << endl;
  for (int i = 0; i < 99; i++)
jcaceres's avatar
jcaceres committed
456
457
458
  if ((info->
  sampleRates[i] > 0) && (info->sampleRates[i] < 200000))
  cout << info->sampleRates[i] << endl;
jcaceres's avatar
jcaceres committed
459
  cout << endl;
jcaceres's avatar
jcaceres committed
460
  }
jcaceres's avatar
jcaceres committed
461
*/
jcaceres's avatar
jcaceres committed
462
463
464
465

void
AudioDevice::setThreads (streamThreads t)
{
jcaceres's avatar
jcaceres committed
466
467
  inThread = t.audioin;
  outThread = t.audioout;
jcaceres's avatar
jcaceres committed
468
}