CSL 5.2

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

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
25 PAIO::~PAIO() {
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
87 if (mInputBuffer.mAreBuffersAllocated)
88 mInputBuffer.freeBuffers();
89 mInputBuffer.allocateBuffers();
90 if (mOutputBuffer.mAreBuffersAllocated)
91 mOutputBuffer.freeBuffers();
92 mOutputBuffer.allocateBuffers();
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(),
100 CGestalt::blockSize(),
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);
133 mStatus = kIORunning;
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);
167 if (mOutputBuffer.mAreBuffersAllocated)
168 mOutputBuffer.freeBuffers();
169 if (mInputBuffer.mAreBuffersAllocated)
170 mInputBuffer.freeBuffers();
171 Pa_Terminate();
172 mStatus = kIOInit;
173 }
174
175 // test the IO's graph
176
177 void PAIO::test() throw(CException) {
178 Buffer outBuffer(CGestalt::numOutChannels(), CGestalt::blockSize());
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())
203 CGestalt::setFrameRate(sr);
204 if (bs != CGestalt::blockSize())
205 CGestalt::setBlockSize(bs);
206 // set the global # IO channels
207 if (ic != CGestalt::numInChannels())
208 CGestalt::setNumInChannels(ic);
209 mNumInChannels = ic;
210 if (oc != CGestalt::numOutChannels())
211 CGestalt::setNumOutChannels(oc);
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;
228 mNumRealOutChannels = 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));
252 mInputParameters->channelCount = csl_min(mNumRealInChannels, CGestalt::numInChannels());
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));
264 mOutputParameters->channelCount = csl_min(mNumRealOutChannels, CGestalt::numOutChannels());
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 }