CSL  5.2
PAIO.cpp
Go to the documentation of this file.
1 //
2 // PAIO.cpp -- DAC IO using PortAudio
3 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
4 //
5 
6 #include "PAIO.h"
7 #include <iostream>
8 
9 using namespace csl;
10 
11 // Constructors
12 
13 //PAIO::PAIO() : IO() {
14 // Pa_Initialize();
15 // mStatus = kIONew;
16 //}
17 
18 PAIO::PAIO(unsigned sr, unsigned bs, int in_stream, int out_stream, unsigned in_chans, unsigned out_chans)
19  : IO(sr, bs, in_stream, out_stream, in_chans, out_chans) {
20  Pa_Initialize();
21  mStatus = kIONew;
22  this->initialize(sr, bs, in_stream, out_stream, in_chans, out_chans);
23 }
24 
26 //#ifdef CSL_DEBUG
27  logMsg("PAIO::destructor");
28 //#endif
29  if (mStatus == kIORunning)
30  stop();
31  if (mStatus == kIOOpen)
32  close();
33  if (mInputParameters != NULL)
34  free(mInputParameters);
35  if (mOutputParameters != NULL)
36  free(mOutputParameters);
37 }
38 
39 // The PortAudio callback function
40 
41 static int pa_callback (const void * inputPointer, void * outputPointer,
42  unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *outTime,
43  PaStreamCallbackFlags statusFlags, void * userData) {
44  PAIO * paio = (PAIO *) userData; // cast the user data pointer as a PAIO guy
45  sample * out = (float *) outputPointer;
46  if (paio->mStatus != kIORunning)
47  return 0;
48  if (paio->mGraph == 0) { // if there's no input graph, just play silence...
49 // memset(out, 0, (framesPerBuffer * numOutChannels * sizeof(sample)));
50  return 0;
51  }
52  Buffer * outBuffer = & (paio->mOutputBuffer);
53 // unsigned numOutChannels = outBuffer->mNumChannels;
54  outBuffer->mNumFrames = framesPerBuffer;
55 #ifdef CSL_DEBUG
56  logMsg("PAIO::callback (%x)", paio->mGraph);
57 #endif
58  paio->mNumFramesPlayed += framesPerBuffer;
59  if (paio->mNumInChannels > 0) {
60  paio->mInputPointer = (float *) inputPointer; // get the I/O buffer pointers
61  paio->mInputBuffer.mNumFrames = framesPerBuffer; // interleaved
62  }
63  paio->pullInput(*outBuffer, out);
64  return 0;
65 }
66 
67 // PA error handler
68 
69 void PAIO::handleError(PaError err) throw(CException) {
70  Pa_Terminate();
71  logMsg(kLogError, "An error occured while using PortAudio");
72  logMsg(kLogError, "Error number: %d", err);
73  logMsg(kLogError, "Error message: %s", Pa_GetErrorText(err));
74  throw IOError("Portaudio error");
75 }
76 
77 // Open port audio
78 
79 void PAIO::open() throw(CException) {
80  PaError err;
81  if (mStatus == kIOOpen) // already open
82  return;
83  if (mStatus != kIOInit) {
84  logMsg(kLogError, "Error opening PortAudio in state %d\n", mStatus);
85  return;
86  } // Allocate the IO buffers
93 //#ifdef CSL_DEBUG
94  logMsg("PAIO::open");
95 //#endif
96  err = Pa_OpenStream (&mStream, // the big open_stream call
97  mInputParameters, // set up input device
98  mOutputParameters, // set up output device
99  (double) CGestalt::frameRate(),
101  0, // stream flags
102  pa_callback, // our callback function (see above)
103  this); // user data = this PAIO object
104 
105  if (err != paNoError) {
106 #ifdef CSL_DEBUG
107  logMsg("Error opening PortAudio: %s", Pa_GetErrorText(err ));
108 #endif
109  throw IOError("Error opening PortAudio");
110  }
111 #ifdef DO_TIMING
112  mThisSec = mTimeVals = mTimeSum = 0;
113 #endif
114  mStatus = kIOOpen;
115 }
116 
117 // Start PA IO
118 
119 void PAIO::start() throw(CException) {
120  PaError err;
121  if (mStatus == kIORunning) // already running
122  return;
123  if (mStatus != kIOOpen) { // wrong state
124  logMsg(kLogError, "Error starting PortAudio in state %d\n", mStatus);
125  return;
126  }
127 //#ifdef CSL_DEBUG
128  logMsg("PAIO::start");
129 //#endif
130  err = Pa_StartStream(mStream);
131  if (err != paNoError)
132  handleError(err);
134 }
135 
136 // Stop
137 
138 void PAIO::stop() throw(CException) {
139  PaError err;
140  if (mStatus != kIORunning) {
141  logMsg(kLogError, "Error stopping PortAudio in state %d\n", mStatus);
142  return;
143  }
144 //#ifdef CSL_DEBUG
145  logMsg("PAIO::stop");
146 //#endif
147  err = Pa_StopStream(mStream);
148  if (err != paNoError)
149  handleError(err);
150  mStatus = kIOOpen;
151 }
152 
153 // Close
154 
155 void PAIO::close() throw(CException) {
156  PaError err;
157  if (mStatus != kIOOpen) {
158  logMsg(kLogError, "Error closing PortAudio in state %d\n", mStatus);
159  return;
160  }
161 //#ifdef CSL_DEBUG
162  logMsg("PAIO::close");
163 //#endif
164  err = Pa_CloseStream(mStream);
165  if (err != paNoError)
166  return handleError(err);
171  Pa_Terminate();
172  mStatus = kIOInit;
173 }
174 
175 // test the IO's graph
176 
177 void PAIO::test() throw(CException) {
179  outBuffer.allocateBuffers();
180 #ifdef CSL_DEBUG
181  logMsg("PAIO::test");
182 #endif
183  if (mGraph != 0) {
184  try {
185  mGraph->nextBuffer(outBuffer);
186  } catch (CException ex) { // handler: log error and play silence
187  logMsg(kLogError, "An error occured in the CSL nextBuffer method: %s\n", ex.mMessage.c_str());
188  throw ex; // swallow the error
189  }
190  }
191 }
192 
193 // Protected init method does all the set up and device allocation
194 
195 void PAIO::initialize(unsigned sr, unsigned bs, int is, int os, unsigned ic, unsigned oc) {
196  const PaDeviceInfo *pdi;
197  IODevice * devPtr;
198  // set the global frame rate and block size
199 //#ifdef CSL_DEBUG
200  logMsg("PAIO::init");
201 //#endif
202  if (sr != CGestalt::frameRate())
204  if (bs != CGestalt::blockSize())
206  // set the global # IO channels
207  if (ic != CGestalt::numInChannels())
209  mNumInChannels = ic;
210  if (oc != CGestalt::numOutChannels())
212  mNumOutChannels = oc;
213 
214  if (is < 0)
215  is = (int)Pa_GetDefaultInputDevice();
216  if (os < 0)
217  os = (int)Pa_GetDefaultOutputDevice();
218 
219  mChannelMap = new unsigned[mNumOutChannels]; // Allocate memory for the channel map
220 
221  for(unsigned i = 0; i < mNumOutChannels; i++) // initialize mapping all channels to themselves.
222  mChannelMap[i] = i;
223 
224  mInputBuffer.setSize(ic, bs); // set receiver's buffers
225  mOutputBuffer.setSize(oc, bs);
226 
227  mNumRealInChannels = 0;
229  PaDeviceIndex numDevices = Pa_GetDeviceCount(); // count the PA devices
230  if (numDevices < 0 )
231  logMsg(kLogFatal, "Pa_CountDevices returned 0x%x\n", numDevices);
232  // iterate over the devices, adding PADevices to the mDevices vector
233  for (int i = 0; i < numDevices; i++) {
234  pdi = Pa_GetDeviceInfo(i);
235  devPtr = new IODevice(pdi->name, i,
236  pdi->maxInputChannels, pdi->maxOutputChannels,
237  (i == (int)Pa_GetDefaultInputDevice()), (i == (int)Pa_GetDefaultOutputDevice()));
238  devPtr->mFrameRates.push_back(pdi->defaultSampleRate); // V19 mod
239  mDevices.push_back(devPtr);
240  mNumRealInChannels += pdi->maxInputChannels;
241  mNumRealOutChannels += pdi->maxOutputChannels;
242  }
243 
244  // set the receiver's state variables
245  if (ic == 0) { // Port Audio can't open a device with 0 channels
246  mInDev = paNoDevice; // In such case, "No device" has to be specified.
247  mInputParameters = NULL;
248  } else {
249  mInDev = is;
250  // setup input/output params. portaudio V19 change
251  mInputParameters = (PaStreamParameters*)malloc(sizeof(PaStreamParameters));
253  mInputParameters->device = mInDev;
254  mInputParameters->sampleFormat = paFloat32;
255  mInputParameters->suggestedLatency = (PaTime) 0;
256  mInputParameters->hostApiSpecificStreamInfo = NULL;
257  }
258  if (oc == 0) { // Port Audio can't open a device with 0 channels
259  mOutDev = paNoDevice; // In such case, "No device" has to be specified.
260  mOutputParameters = NULL;
261  } else {
262  mOutDev = os;
263  mOutputParameters = (PaStreamParameters*)malloc(sizeof(PaStreamParameters));
265  mOutputParameters->device = mOutDev;
266  mOutputParameters->sampleFormat = paFloat32;
267  mOutputParameters->suggestedLatency = (PaTime) 0;
268  mOutputParameters->hostApiSpecificStreamInfo = NULL;
269  }
270  // print out the device table
271  logMsg("PAIO set-up %d in, %d out", mNumInChannels, mNumOutChannels);
272  mStatus = kIOInit; // set status flag
273 //#ifdef CSL_DEBUG
274  logMsg(" Found %d PortAudio devices: %d in, %d out", numDevices, mNumRealInChannels, mNumRealOutChannels);
275  for (unsigned i = 0; i < mDevices.size(); i++) {
276  mDevices[i]->dump();
277  }
278 //#endif
279 }