CLAM-Development  1.4.0
PANetworkPlayer.cxx
Go to the documentation of this file.
1 
2 #include "PANetworkPlayer.hxx"
3 #include "PushFlowControl.hxx"
4 
5 #include "AudioSink.hxx"
6 #include "AudioSource.hxx"
7 
8 #include <pthread.h>
9 
10 
11 namespace CLAM
12 {
13 
14 
15 int PANetworkPlayer::ProcessCallback (
16  const void *inputBuffers,
17  void *outputBuffers,
18  unsigned long framesPerBuffer,
19  const PaStreamCallbackTimeInfo* timeInfo,
20  PaStreamCallbackFlags statusFlags,
21  void *userData
22  )
23 {
24  if (statusFlags)
25  {
26  if (statusFlags & paOutputUnderflow)
27  std::cerr << "Portaudio backend: Output Underflow" << std::endl;
28  if (statusFlags & paInputUnderflow)
29  std::cerr << "Portaudio backend: Input Underflow" << std::endl;
30  if (statusFlags & paOutputOverflow)
31  std::cerr << "Portaudio backend: Output Overflow" << std::endl;
32  if (statusFlags & paInputOverflow)
33  std::cerr << "Portaudio backend: Input Overflow" << std::endl;
34  if (statusFlags & paPrimingOutput)
35  std::cerr << "Portaudio backend: Priming Output" << std::endl;
36  }
37  PANetworkPlayer* player=(PANetworkPlayer*)userData;
38 
39  player->Do(inputBuffers, outputBuffers, framesPerBuffer);
40 
41  return 0;
42 }
43 
44 namespace {
45 
46 void displayPADevices()
47 {
48  int howManiApis = Pa_GetHostApiCount();
49  int defaultApi = Pa_GetDefaultHostApi();
50  std::cout << "Default API " << defaultApi << std::endl;
51  for (int api=0; api<howManiApis; api++)
52  {
53  const PaHostApiInfo * apiInfo = Pa_GetHostApiInfo( api );
54  std::cout
55  << (api==defaultApi?"* ":" ")
56  << apiInfo->name
57  << " (" << api << ")"
58  << std::endl;
59  for (int device=0; device<apiInfo->deviceCount; device++)
60  {
61  int fullDevice = Pa_HostApiDeviceIndexToDeviceIndex(api, device);
62  const PaDeviceInfo * deviceInfo = Pa_GetDeviceInfo(fullDevice);
63  std::cout << "\t"
64  << " (" << fullDevice << "/" << device << ") "
65  << (fullDevice == Pa_GetDefaultInputDevice()? "*<": " ")
66  << (fullDevice == Pa_GetDefaultOutputDevice()? "*>": " ")
67  << (fullDevice == apiInfo->defaultInputDevice?"*<":" ")
68  << (fullDevice == apiInfo->defaultOutputDevice?"*>":" ")
69  << deviceInfo->name
70  << " Inputs: " << deviceInfo->maxInputChannels
71  << " Outputs: " << deviceInfo->maxOutputChannels
72  << std::endl;
73  }
74  }
75 }
76 
77 }
78 
80  : mPreferredBufferSize(paFramesPerBufferUnspecified)
81  , mSamplingRate(44100)
82  , mPortAudioStream(0)
83  , mError(paNoError)
84 {
85 }
86 
88 {
89  Stop();
90 }
91 
93 {
94  if (IsPlaying()) return;
95  if (IsPaused())
96  {
97  BePlaying();
98  return;
99  }
100  if (CheckPaError(Pa_Initialize())) return;
101  displayPADevices();
102 
104 
105  int nInChannels = GetNSources();
106  int nOutChannels = GetNSinks();
107 
108  PaHostApiTypeId apiTryList[] = {
109  paDirectSound,
110  paMME,
111  paASIO,
112  paSoundManager,
113  paCoreAudio,
114  paALSA,
115  paAL,
116  paBeOS,
117  paWDMKS,
118  paJACK,
119 // paWASAPI,
120 // paAudioScienceHPI,
121  paOSS,
122  paInDevelopment
123  };
124 
125 // int defaultApi = Pa_GetDefaultHostApi();
126 // const PaHostApiInfo * apiInfo = Pa_GetHostApiInfo( defaultApi );
127  const PaHostApiInfo * apiInfo = 0;
128  for (unsigned i=0; apiTryList[i]!=paInDevelopment; i++)
129  {
130  PaHostApiIndex apiIndex = Pa_HostApiTypeIdToHostApiIndex(apiTryList[i]);
131  std::cerr << apiIndex << std::endl;
132  if (apiIndex<0) continue;
133  apiInfo = Pa_GetHostApiInfo( apiIndex );
134  std::cerr << "Portaudio Chosen API: " << apiInfo->name << " " << apiIndex << std::endl;
135  break;
136  }
137  CLAM_ASSERT(apiInfo, "PortAudio: No API available.");
138 
139  //Create configuration for input&output and then register the stream
140  PaStreamParameters inputParameters;
141  PaStreamParameters * inParams = 0;
142  if (nInChannels)
143  {
144  inputParameters.device = apiInfo->defaultInputDevice;
145  if ( inputParameters.device == paNoDevice )
146  {
147  mErrorMessage = "No free default input device";
148  std::cerr << "PortAudio Error: " << mErrorMessage << std::endl;
149  return;
150  }
151  const PaDeviceInfo * info = Pa_GetDeviceInfo( inputParameters.device );
152  std::cerr << "PortAudio: Chosen Input: " << info->name << std::endl;
153  if (nInChannels > Pa_GetDeviceInfo( inputParameters.device )->maxInputChannels)
154  {
155  mErrorMessage = "Too many input channels for the default device";
156  std::cerr << "PortAudio Error: " << mErrorMessage << std::endl;
157  return;
158  }
159  inputParameters.channelCount = nInChannels;
160  inputParameters.sampleFormat = paFloat32 | paNonInterleaved ; /* 32 bit floating point output, having non-interleaved samples*/
161  inputParameters.suggestedLatency = info->defaultLowOutputLatency;
162  inputParameters.hostApiSpecificStreamInfo = NULL;
163  inParams = &inputParameters;
164  }
165 
166  PaStreamParameters outputParameters;
167  PaStreamParameters * outParams = 0;
168  if (nOutChannels)
169  {
170  outputParameters.device = apiInfo->defaultOutputDevice;
171  if ( outputParameters.device == paNoDevice )
172  {
173  mErrorMessage = "No free default output device";
174  std::cerr << "PortAudio Error: " << mErrorMessage << std::endl;
175  return;
176  }
177  const PaDeviceInfo * info = Pa_GetDeviceInfo( outputParameters.device );
178  std::cerr << "PortAudio: Chosen Output: " << info->name << std::endl;
179  if (nOutChannels > Pa_GetDeviceInfo( outputParameters.device )->maxOutputChannels)
180  {
181  mErrorMessage = "Too many output channels for the default device";
182  std::cerr << "PortAudio Error: " << mErrorMessage << std::endl;
183  return;
184  }
185  outputParameters.channelCount = nOutChannels;
186  outputParameters.sampleFormat = paFloat32 | paNonInterleaved ; /* 32 bit floating point output, having non-interleaved samples */
187  outputParameters.suggestedLatency = info->defaultLowOutputLatency;
188  outputParameters.hostApiSpecificStreamInfo = NULL;
189  outParams = &outputParameters;
190  }
191 
192  CLAM_ASSERT(!mPortAudioStream, "Portaudio: Previous stream not closed");
193  if (CheckPaError(
194  Pa_OpenStream(
195  &mPortAudioStream,
196  inParams,
197  outParams,
198  double(mSamplingRate),
199  mPreferredBufferSize,
200  paClipOff, /* we won't output out of range samples so don't bother clipping them */
201  ProcessCallback,
202  this )
203  ))
204  {
205  mErrorMessage = "Audio i/o devices requirements not fullfilled";
206  return;
207  }
208 
209  BePlaying();
210  const PaStreamInfo * streamInfo = Pa_GetStreamInfo(mPortAudioStream);
211  std::cout << "Sample rate: " << streamInfo->sampleRate << std::endl;
212  std::cout << "Input latency: " << streamInfo->inputLatency << std::endl;
213  std::cout << "Output latency: " << streamInfo->outputLatency << std::endl;
214 
215  mNeedsPriority=true;
216  Pa_StartStream( mPortAudioStream );
217 }
218 
220 {
221  if (IsStopped()) return;
222  if ( mPortAudioStream )
223  {
224  Pa_StopStream( mPortAudioStream );
225  CheckPaError( Pa_CloseStream( mPortAudioStream ) );
226  mPortAudioStream=0;
227  }
228  BeStopped();
229  Pa_Terminate();
230 }
231 
233 {
234  return mError==paNoError;
235 }
236 
238 {
239  return mErrorMessage;
240 }
241 
242 bool PANetworkPlayer::CheckPaError(PaError result)
243 {
244  mError = result;
245  if( result == paNoError ) return false;
246  mErrorMessage = Pa_GetErrorText(mError);
247  std::cerr
248  << "PortAudio Error #" << result << ": "
249  << Pa_GetErrorText( result ) << std::endl;
250  return true;
251 }
252 
253 void PANetworkPlayer::Do(const void *inputBuffers, void *outputBuffers,
254  unsigned long framesPerBuffer)
255 {
256  if (IsStopped()) return;
257  if (mNeedsPriority)
258  {
259  mNeedsPriority = false;
260  #ifdef TODO__was_WIN32
261  BOOL res;
262  DWORD err;
263 
264  res = SetPriorityClass(GetCurrentProcess(),NORMAL_PRIORITY_CLASS );
265  err = GetLastError();
266  res = SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL );
267  err = GetLastError();
268  #else
269  struct sched_param sched_param;
270  int policy;
271 
272  if (pthread_getschedparam(pthread_self(), &policy, &sched_param) < 0)
273  std::cerr << "Scheduler getparam failed..." << std::endl;
274  sched_param.sched_priority = sched_get_priority_max(SCHED_RR)-1;
275  if (!pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param))
276  std::cerr << "Scheduler set to Round Robin with priority "<< sched_param.sched_priority << std::endl;
277  #endif
278  }
279  if (IsPaused())
280  {
281  MuteOutBuffers((float**) outputBuffers, framesPerBuffer);
282  return;
283  }
284  DoInPorts( (float**) inputBuffers, framesPerBuffer);
285  DoOutPorts( (float**) outputBuffers, framesPerBuffer);
286  GetNetwork().Do();
287 }
288 
289 void PANetworkPlayer::DoInPorts(float** input, unsigned long nframes)
290 {
291  for(unsigned i = 0; i < GetNSources(); ++i)
292  {
293  SetSourceBuffer(i, input[i], nframes);
294  }
295 }
296 
297 void PANetworkPlayer::DoOutPorts(float** output, unsigned long nframes)
298 {
299  for (unsigned i=0; i<GetNSinks(); ++i)
300  {
301  SetSinkBuffer(i, output[i], nframes);
302  }
303 }
304 
305 void PANetworkPlayer::MuteOutBuffers(float** output, unsigned long nframes)
306 {
307  unsigned nSinks = GetNSinks();
308  for (unsigned i=0; i<nSinks; i++)
309  std::memset(output[i], 0, nframes*sizeof(float));
310 }
311 
312 } //end namespace CLAM
313