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