1 /*
2 Copyright (C) 2007 Remon Sijrier
3
4 This file is part of Traverso
5
6 Traverso is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 */
21
22 #include "PADriver.h"
23
24 #include "AudioDevice.h"
25 #include "AudioChannel.h"
26
27 #include <Utils.h>
28
29 // Always put me below _all_ includes, this is needed
30 // in case we run with memory leak detection enabled!
31 #include "Debugger.h"
32
33
34 // TODO Is there an xrun callback for PortAudio? If so, connect to _xrun_callback
35 // TODO If there is some portaudio shutdown callback, connect to _on_pa_shutdown_callback
36 // and make it work!
37
PADriver(AudioDevice * dev,int rate,nframes_t bufferSize)38 PADriver::PADriver( AudioDevice * dev , int rate, nframes_t bufferSize)
39 : Driver(dev, rate, bufferSize)
40 {
41 read = MakeDelegate(this, &PADriver::_read);
42 write = MakeDelegate(this, &PADriver::_write);
43 run_cycle = RunCycleCallback(this, &PADriver::_run_cycle);
44 }
45
~PADriver()46 PADriver::~PADriver( )
47 {
48 PENTER;
49
50 }
51
_read(nframes_t nframes)52 int PADriver::_read(nframes_t nframes)
53 {
54 float* in = (float*) paInputBuffer;
55
56 if (!captureChannels.size()) {
57 return 0;
58 }
59
60 if (!captureChannels.at(0)->has_data()) {
61 return 0;
62 }
63
64 float* datachan0 = captureChannels.at(0)->get_data();
65 float* datachan1 = captureChannels.at(1)->get_data();
66
67 int j=0;
68
69 for (uint i=0; i<nframes*2; i++) {
70 datachan0[j] = in[i++];
71 datachan1[j] = in[i];
72 j++;
73 }
74
75 return 1;
76 }
77
_write(nframes_t nframes)78 int PADriver::_write(nframes_t nframes)
79 {
80 // TODO use the found maxchannel count instead of assuming 2 !! ( == playbackChannels.size() )
81 // Properly iterate over all channel buffers to mixdown the audio
82 // in an interleaved way (since our channel buffers represent just that,
83 // one channel, no interleaved data there)
84
85 // CRITICAL : When messing with this routine, be sure to do it right, at the
86 // least, turn the volume of your speakers down, if you want them and your ears
87 // to last a little longer :D
88
89 if (!playbackChannels.size()) {
90 return 0;
91 }
92
93 float* out = (float*) paOutputBuffer;
94 float* datachan0 = playbackChannels.at(0)->get_data();
95 float* datachan1 = playbackChannels.at(1)->get_data();
96
97 int j=0;
98
99 for (uint i=0; i<nframes*2; i++) {
100 out[i++] = datachan0[j];
101 out[i] = datachan1[j];
102 j++;
103 }
104
105 playbackChannels.at(0)->silence_buffer(nframes);
106 playbackChannels.at(1)->silence_buffer(nframes);
107
108 return 1;
109 }
110
setup(bool capture,bool playback,const QString & hostapi)111 int PADriver::setup(bool capture, bool playback, const QString& hostapi)
112 {
113 // TODO Only open the capture/playback stream if requested (capture == true, playback == true)
114
115 // TODO use hostapi to detect which hostApi to use.
116 // hostapi can be any of these:
117 // Linux: alsa, jack, oss
118 // Mac os x: coreaudio, jack
119 // Windows: wmme, directx, asio
120
121 // TODO In case of hostapi == "alsa", the callback thread prio needs to be set to realtime.
122 // there has been some discussion on this on the pa mailinglist, digg it up!
123
124 printf("PADriver:: capture, playback, hostapi: %d, %d, %s\n", capture, playback, QS_C(hostapi));
125
126 PaError err = Pa_Initialize();
127
128 if( err != paNoError ) {
129 device->message(tr("PADriver:: PortAudio error: %1").arg(Pa_GetErrorText( err )), AudioDevice::WARNING);
130 Pa_Terminate();
131 return -1;
132 } else {
133 printf("PADriver:: Succesfully initialized portaudio\n");
134 }
135
136
137 PaStreamParameters outputParameters, inputParameters;
138 PaDeviceIndex deviceindex = -1;
139
140 for (int i=0; i<Pa_GetHostApiCount(); ++i) {
141 const PaHostApiInfo* inf = Pa_GetHostApiInfo(i);
142
143 // device->message(tr("hostapi name is %1, deviceCount is %2").arg(inf->name).arg(inf->deviceCount), AudioDevice::INFO);
144
145 if (hostapi == "alsa" && inf->type == paALSA) {
146 printf("PADriver:: Found alsa host api, using device %d\n", i);
147 deviceindex = i;
148 break;
149 }
150
151 if (hostapi == "jack" && inf->type == paJACK) {
152 printf("PADriver:: Found jack host api, using device %d\n", i);
153 deviceindex = i;
154 break;
155 }
156
157 if (hostapi == "wmme" && inf->type == paMME) {
158 printf("PADriver:: Found wmme host api, using device %d\n", i);
159 deviceindex = i;
160 break;
161 }
162
163 if (hostapi == "directsound" && inf->type == paDirectSound ) {
164 printf("PADriver:: Found directsound host api, using device %d\n", i);
165 deviceindex = i;
166 break;
167 }
168
169 if (hostapi == "coreaudio" && inf->type == paCoreAudio ) {
170 printf("PADriver:: Found directsound host api, using device %d\n", i);
171 deviceindex = i;
172 break;
173 }
174 }
175
176
177 if (deviceindex == -1) {
178 device->message(tr("PADriver:: hostapi %1 was not found by Portaudio!").arg(hostapi), AudioDevice::WARNING);
179 return -1;
180 }
181
182 deviceindex = 0;
183 // device->message(tr("PADriver:: using device %1").arg(deviceindex), AudioDevice::INFO);
184
185 // Configure output parameters.
186 // TODO get the max channel count, and use that instead, of assuming 2
187 PaDeviceIndex result = Pa_GetDefaultOutputDevice();
188 if( result != paNoDevice) {
189 outputParameters.device = result;
190 outputParameters.channelCount = 2;
191 outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
192 outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
193 outputParameters.hostApiSpecificStreamInfo = NULL;
194 }
195
196 result = Pa_GetDefaultInputDevice();
197 if( result != paNoDevice) {
198 inputParameters.device = result;
199 inputParameters.channelCount = 2;
200 inputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
201 inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
202 inputParameters.hostApiSpecificStreamInfo = NULL;
203 }
204
205 /* Open an audio I/O stream. */
206 err = Pa_OpenStream(
207 &m_paStream,
208 &inputParameters, // The input parameter
209 &outputParameters, // The outputparameter
210 frame_rate, // Set in the constructor
211 frames_per_cycle, // Set in the constructor
212 paNoFlag, // Don't use any flags
213 _process_callback, // our callback function
214 this );
215
216 if( err != paNoError ) {
217 device->message(tr("PADriver:: PortAudio error: %1").arg(Pa_GetErrorText( err )), AudioDevice::WARNING);
218 Pa_Terminate();
219 return -1;
220 } else {
221 printf("PADriver:: Succesfully opened portaudio stream\n");
222 }
223
224 AudioChannel* audiochannel;
225 int port_flags;
226 char buf[32];
227
228 // TODO use the found maxchannel count for the playback stream, instead of assuming 2 !!
229 for (int chn = 0; chn < 2; chn++) {
230
231 snprintf (buf, sizeof(buf) - 1, "playback_%d", chn+1);
232
233 audiochannel = device->register_playback_channel(buf, "32 bit float audio", port_flags, frames_per_cycle, chn);
234 audiochannel->set_latency( frames_per_cycle + capture_frame_latency );
235 playbackChannels.append(audiochannel);
236 }
237
238 // TODO use the found maxchannel count for the capture stream, instead of assuming 0 !!
239 for (int chn = 0; chn < 2; chn++) {
240
241 snprintf (buf, sizeof(buf) - 1, "capture_%d", chn+1);
242
243 audiochannel = device->register_capture_channel(buf, "32 bit float audio", port_flags, frames_per_cycle, chn);
244 audiochannel->set_latency( frames_per_cycle + capture_frame_latency );
245 captureChannels.append(audiochannel);
246 }
247
248 return 1;
249 }
250
attach()251 int PADriver::attach()
252 {
253 return 1;
254 }
255
start()256 int PADriver::start( )
257 {
258 PENTER;
259
260 PaError err = Pa_StartStream( m_paStream );
261
262 if( err != paNoError ) {
263 device->message((tr("PADriver:: PortAudio error: %1").arg(Pa_GetErrorText( err ))), AudioDevice::WARNING);
264 Pa_Terminate();
265 return -1;
266 } else {
267 printf("PADriver:: Succesfully started portaudio stream\n");
268 }
269
270 return 1;
271 }
272
stop()273 int PADriver::stop( )
274 {
275 PENTER;
276 PaError err = Pa_CloseStream( m_paStream );
277
278 if( err != paNoError ) {
279 device->message((tr("PADriver:: PortAudio error: %1").arg(Pa_GetErrorText( err ))), AudioDevice::WARNING);
280 Pa_Terminate();
281 } else {
282 printf("PADriver:: Succesfully closed portaudio stream\n\n");
283 }
284
285 return 1;
286 }
287
process_callback(nframes_t nframes)288 int PADriver::process_callback (nframes_t nframes)
289 {
290 if (device->run_cycle( nframes, 0.0) == -1) {
291 return paAbort;
292 }
293
294 return paContinue;
295 }
296
get_device_name()297 QString PADriver::get_device_name( )
298 {
299 // TODO get it from portaudio ?
300 return "AudioDevice";
301 }
302
get_device_longname()303 QString PADriver::get_device_longname( )
304 {
305 // TODO get it from portaudio ?
306 return "AudioDevice";
307 }
308
_xrun_callback(void * arg)309 int PADriver::_xrun_callback( void * arg )
310 {
311 PADriver* driver = static_cast<PADriver *> (arg);
312 driver->device->xrun();
313 return 0;
314 }
315
_on_pa_shutdown_callback(void * arg)316 void PADriver::_on_pa_shutdown_callback(void * arg)
317 {
318 Q_UNUSED(arg);
319 }
320
_process_callback(const void * inputBuffer,void * outputBuffer,unsigned long framesPerBuffer,const PaStreamCallbackTimeInfo * timeInfo,PaStreamCallbackFlags statusFlags,void * arg)321 int PADriver::_process_callback(
322 const void *inputBuffer,
323 void *outputBuffer,
324 unsigned long framesPerBuffer,
325 const PaStreamCallbackTimeInfo* timeInfo,
326 PaStreamCallbackFlags statusFlags,
327 void *arg )
328 {
329 Q_UNUSED(timeInfo);
330 Q_UNUSED(statusFlags);
331
332 PADriver* driver = static_cast<PADriver *> (arg);
333
334 driver->paInputBuffer = inputBuffer;
335 driver->paOutputBuffer = outputBuffer;
336
337 driver->process_callback (framesPerBuffer);
338
339 return 0;
340 }
341
get_cpu_load()342 float PADriver::get_cpu_load( )
343 {
344 return Pa_GetStreamCpuLoad(m_paStream) * 100;
345 }
346
347
348 //eof
349