1 /*
2  * Portable Audio I/O Library
3  * Java Binding for PortAudio
4  *
5  * Based on the Open Source API proposed by Ross Bencina
6  * Copyright (c) 2008 Ross Bencina
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining
9  * a copy of this software and associated documentation files
10  * (the "Software"), to deal in the Software without restriction,
11  * including without limitation the rights to use, copy, modify, merge,
12  * publish, distribute, sublicense, and/or sell copies of the Software,
13  * and to permit persons to whom the Software is furnished to do so,
14  * subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
23  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 /*
29  * The text above constitutes the entire PortAudio license; however,
30  * the PortAudio community also makes the following non-binding requests:
31  *
32  * Any person wishing to distribute modifications to the Software is
33  * requested to send the modifications to the original developer so that
34  * they can be incorporated into the canonical version. It is also
35  * requested that these non-binding requests be included along with the
36  * license above.
37  */
38 
39 package com.portaudio;
40 
41 import junit.framework.TestCase;
42 
43 /**
44  * Test the Java bindings for PortAudio.
45  *
46  * @author Phil Burk
47  *
48  */
49 public class TestBasic extends TestCase
50 {
51 
testDeviceCount()52 	public void testDeviceCount()
53 	{
54 		PortAudio.initialize();
55 		assertTrue( "version invalid", (PortAudio.getVersion() > 0) );
56 		System.out.println( "getVersion  = " + PortAudio.getVersion() );
57 		System.out.println( "getVersionText  = " + PortAudio.getVersionText() );
58 		System.out.println( "getDeviceCount  = " + PortAudio.getDeviceCount() );
59 		assertTrue( "getDeviceCount", (PortAudio.getDeviceCount() > 0) );
60 		PortAudio.terminate();
61 	}
62 
testListDevices()63 	public void testListDevices()
64 	{
65 		PortAudio.initialize();
66 		int count = PortAudio.getDeviceCount();
67 		assertTrue( "getDeviceCount", (count > 0) );
68 		for( int i = 0; i < count; i++ )
69 		{
70 			DeviceInfo info = PortAudio.getDeviceInfo( i );
71 			System.out.println( "------------------ #" + i );
72 			System.out.println( "  name              = " + info.name );
73 			System.out.println( "  hostApi           = " + info.hostApi );
74 			System.out.println( "  maxOutputChannels = "
75 					+ info.maxOutputChannels );
76 			System.out.println( "  maxInputChannels  = "
77 					+ info.maxInputChannels );
78 			System.out.println( "  defaultSampleRate = "
79 					+ info.defaultSampleRate );
80 			System.out.printf( "  defaultLowInputLatency   = %3d msec\n",
81 					((int) (info.defaultLowInputLatency * 1000)) );
82 			System.out.printf( "  defaultHighInputLatency  = %3d msec\n",
83 					((int) (info.defaultHighInputLatency * 1000)) );
84 			System.out.printf( "  defaultLowOutputLatency  = %3d msec\n",
85 					((int) (info.defaultLowOutputLatency * 1000)) );
86 			System.out.printf( "  defaultHighOutputLatency = %3d msec\n",
87 					((int) (info.defaultHighOutputLatency * 1000)) );
88 
89 			assertTrue( "some channels",
90 					(info.maxOutputChannels + info.maxInputChannels) > 0 );
91 			assertTrue( "not too many channels", (info.maxInputChannels < 64) );
92 			assertTrue( "not too many channels", (info.maxOutputChannels < 64) );
93 		}
94 
95 		System.out.println( "defaultInput  = "
96 				+ PortAudio.getDefaultInputDevice() );
97 		System.out.println( "defaultOutput = "
98 				+ PortAudio.getDefaultOutputDevice() );
99 
100 		PortAudio.terminate();
101 	}
102 
testHostApis()103 	public void testHostApis()
104 	{
105 		PortAudio.initialize();
106 		int validApiCount = 0;
107 		for( int hostApiType = 0; hostApiType < PortAudio.HOST_API_TYPE_COUNT; hostApiType++ )
108 		{
109 			int hostApiIndex = PortAudio
110 					.hostApiTypeIdToHostApiIndex( hostApiType );
111 			if( hostApiIndex >= 0 )
112 			{
113 				HostApiInfo info = PortAudio.getHostApiInfo( hostApiIndex );
114 				System.out.println( "Checking Host API: " + info.name );
115 				for( int apiDeviceIndex = 0; apiDeviceIndex < info.deviceCount; apiDeviceIndex++ )
116 				{
117 					int deviceIndex = PortAudio
118 							.hostApiDeviceIndexToDeviceIndex( hostApiIndex,
119 									apiDeviceIndex );
120 					DeviceInfo deviceInfo = PortAudio
121 							.getDeviceInfo( deviceIndex );
122 					assertEquals( "host api must match up", hostApiIndex,
123 							deviceInfo.hostApi );
124 				}
125 				validApiCount++;
126 			}
127 		}
128 
129 		assertEquals( "host api counts", PortAudio.getHostApiCount(),
130 				validApiCount );
131 	}
132 
testListHostApis()133 	public void testListHostApis()
134 	{
135 		PortAudio.initialize();
136 		int count = PortAudio.getHostApiCount();
137 		assertTrue( "getHostApiCount", (count > 0) );
138 		for( int i = 0; i < count; i++ )
139 		{
140 			HostApiInfo info = PortAudio.getHostApiInfo( i );
141 			System.out.println( "------------------ #" + i );
142 			System.out.println( "  version             = " + info.version );
143 			System.out.println( "  name                = " + info.name );
144 			System.out.println( "  type                = " + info.type );
145 			System.out.println( "  deviceCount         = " + info.deviceCount );
146 			System.out.println( "  defaultInputDevice  = "
147 					+ info.defaultInputDevice );
148 			System.out.println( "  defaultOutputDevice = "
149 					+ info.defaultOutputDevice );
150 			assertTrue( "some devices", info.deviceCount > 0 );
151 		}
152 
153 		System.out.println( "------\ndefaultHostApi = "
154 				+ PortAudio.getDefaultHostApi() );
155 		PortAudio.terminate();
156 	}
157 
testCheckFormat()158 	public void testCheckFormat()
159 	{
160 		PortAudio.initialize();
161 		StreamParameters streamParameters = new StreamParameters();
162 		streamParameters.device = PortAudio.getDefaultOutputDevice();
163 		int result = PortAudio
164 				.isFormatSupported( null, streamParameters, 44100 );
165 		System.out.println( "isFormatSupported returns " + result );
166 		assertEquals( "default output format", 0, result );
167 		// Try crazy channelCount
168 		streamParameters.channelCount = 8765;
169 		result = PortAudio.isFormatSupported( null, streamParameters, 44100 );
170 		System.out.println( "crazy isFormatSupported returns " + result );
171 		assertTrue( "default output format", (result < 0) );
172 		PortAudio.terminate();
173 	}
174 
175 	static class SineOscillator
176 	{
177 		double phase = 0.0;
178 		double phaseIncrement = 0.01;
179 
SineOscillator(double freq, int sampleRate)180 		SineOscillator(double freq, int sampleRate)
181 		{
182 			phaseIncrement = freq * Math.PI * 2.0 / sampleRate;
183 		}
184 
next()185 		double next()
186 		{
187 			double value = Math.sin( phase );
188 			phase += phaseIncrement;
189 			if( phase > Math.PI )
190 			{
191 				phase -= Math.PI * 2.0;
192 			}
193 			return value;
194 		}
195 	}
196 
testStreamError()197 	public void testStreamError()
198 	{
199 		PortAudio.initialize();
200 		StreamParameters streamParameters = new StreamParameters();
201 		streamParameters.sampleFormat = PortAudio.FORMAT_FLOAT_32;
202 		streamParameters.channelCount = 2;
203 		streamParameters.device = PortAudio.getDefaultOutputDevice();
204 		int framesPerBuffer = 256;
205 		int flags = 0;
206 		BlockingStream stream = PortAudio.openStream( null, streamParameters,
207 				44100, framesPerBuffer, flags );
208 
209 		// Try to write data to a stopped stream.
210 		Throwable caught = null;
211 		try
212 		{
213 			float[] buffer = new float[framesPerBuffer
214 					* streamParameters.channelCount];
215 			stream.write( buffer, framesPerBuffer );
216 		} catch( Throwable e )
217 		{
218 			caught = e;
219 			e.printStackTrace();
220 		}
221 
222 		assertTrue( "caught no expection", (caught != null) );
223 		assertTrue( "exception should say stream is stopped", caught
224 				.getMessage().contains( "stopped" ) );
225 
226 		// Try to write null data.
227 		caught = null;
228 		try
229 		{
230 			stream.write( (float[]) null, framesPerBuffer );
231 		} catch( Throwable e )
232 		{
233 			caught = e;
234 			e.printStackTrace();
235 		}
236 		assertTrue( "caught no expection", (caught != null) );
237 		assertTrue( "exception should say stream is stopped", caught
238 				.getMessage().contains( "null" ) );
239 
240 		// Try to write short data to a float stream.
241 		stream.start();
242 		caught = null;
243 		try
244 		{
245 			short[] buffer = new short[framesPerBuffer
246 					* streamParameters.channelCount];
247 			stream.write( buffer, framesPerBuffer );
248 		} catch( Throwable e )
249 		{
250 			caught = e;
251 			e.printStackTrace();
252 		}
253 
254 		assertTrue( "caught no expection", (caught != null) );
255 		assertTrue( "exception should say tried to", caught.getMessage()
256 				.contains( "Tried to write short" ) );
257 
258 		stream.close();
259 
260 		PortAudio.terminate();
261 	}
262 
checkBlockingWriteFloat( int deviceId, double sampleRate )263 	public void checkBlockingWriteFloat( int deviceId, double sampleRate )
264 	{
265 		StreamParameters streamParameters = new StreamParameters();
266 		streamParameters.channelCount = 2;
267 		streamParameters.device = deviceId;
268 		streamParameters.suggestedLatency = PortAudio
269 				.getDeviceInfo( streamParameters.device ).defaultLowOutputLatency;
270 		System.out.println( "suggestedLatency = "
271 				+ streamParameters.suggestedLatency );
272 
273 		int framesPerBuffer = 256;
274 		int flags = 0;
275 		BlockingStream stream = PortAudio.openStream( null, streamParameters,
276 				(int) sampleRate, framesPerBuffer, flags );
277 		assertTrue( "got default stream", stream != null );
278 
279 		assertEquals( "stream isStopped", true, stream.isStopped() );
280 		assertEquals( "stream isActive", false, stream.isActive() );
281 
282 		int numFrames = 80000;
283 		double expected = ((double)numFrames) / sampleRate;
284 		stream.start();
285 		long startTime = System.currentTimeMillis();
286 		double startStreamTime = stream.getTime();
287 		assertEquals( "stream isStopped", false, stream.isStopped() );
288 		assertEquals( "stream isActive", true, stream.isActive() );
289 
290 		writeSineData( stream, framesPerBuffer, numFrames, (int) sampleRate );
291 
292 		StreamInfo streamInfo = stream.getInfo();
293 		System.out.println( "inputLatency of a stream = "+ streamInfo.inputLatency );
294 		System.out.println( "outputLatency of a stream = "+streamInfo.outputLatency );
295 		System.out.println( "sampleRate of a stream = "+ streamInfo.sampleRate );
296 
297 		assertEquals( "inputLatency of a stream ", 0.0, streamInfo.inputLatency, 0.000001 );
298 		assertTrue( "outputLatency of a stream ",(streamInfo.outputLatency > 0) );
299 		assertEquals( "sampleRate of a stream ", sampleRate, streamInfo.sampleRate, 3 );
300 
301 		double endStreamTime = stream.getTime();
302 		stream.stop();
303 		long stopTime = System.currentTimeMillis();
304 
305 		System.out.println( "startStreamTime = " + startStreamTime );
306 		System.out.println( "endStreamTime = " + endStreamTime );
307 		double elapsedStreamTime = endStreamTime - startStreamTime;
308 		System.out.println( "elapsedStreamTime = " + elapsedStreamTime );
309 		assertTrue( "elapsedStreamTime: " + elapsedStreamTime,
310 				(elapsedStreamTime > 0.0) );
311 		assertEquals( "elapsedStreamTime: ", expected, elapsedStreamTime, 0.10 );
312 
313 		assertEquals( "stream isStopped", true, stream.isStopped() );
314 		assertEquals( "stream isActive", false, stream.isActive() );
315 		stream.close();
316 
317 		double elapsed = (stopTime - startTime) / 1000.0;
318 		assertEquals( "elapsed time to play", expected, elapsed, 0.20 );
319 	}
320 
testBlockingWriteFloat()321 	public void testBlockingWriteFloat()
322 	{
323 		PortAudio.initialize();
324 		checkBlockingWriteFloat( PortAudio.getDefaultOutputDevice(), 44100 );
325 		PortAudio.terminate();
326 	}
327 
ZtestWriteEachHostAPI()328 	public void ZtestWriteEachHostAPI()
329 	{
330 		PortAudio.initialize();
331 		for( int hostApiIndex = 0; hostApiIndex < PortAudio.getHostApiCount(); hostApiIndex++ )
332 		{
333 			HostApiInfo hostInfo = PortAudio.getHostApiInfo( hostApiIndex );
334 			System.out.println( "-------------\nWriting using Host API: " + hostInfo.name );
335 			int deviceId = hostInfo.defaultOutputDevice;
336 			System.out.println( "   Device ID  =" + deviceId );
337 			DeviceInfo deviceInfo = PortAudio.getDeviceInfo( deviceId );
338 			System.out.println( "   sampleRate =" + deviceInfo.defaultSampleRate );
339 			checkBlockingWriteFloat( deviceId,
340 					(int) deviceInfo.defaultSampleRate );
341 			System.out.println( "Finished with " + hostInfo.name );
342 		}
343 		PortAudio.terminate();
344 	}
345 
writeSineData( BlockingStream stream, int framesPerBuffer, int numFrames, int sampleRate )346 	private void writeSineData( BlockingStream stream, int framesPerBuffer,
347 			int numFrames, int sampleRate )
348 	{
349 		float[] buffer = new float[framesPerBuffer * 2];
350 		SineOscillator osc1 = new SineOscillator( 200.0, sampleRate );
351 		SineOscillator osc2 = new SineOscillator( 300.0, sampleRate );
352 		int framesLeft = numFrames;
353 		while( framesLeft > 0 )
354 		{
355 			int index = 0;
356 			int framesToWrite = (framesLeft > framesPerBuffer) ? framesPerBuffer
357 					: framesLeft;
358 			for( int j = 0; j < framesToWrite; j++ )
359 			{
360 				buffer[index++] = (float) osc1.next();
361 				buffer[index++] = (float) osc2.next();
362 			}
363 			stream.write( buffer, framesToWrite );
364 			framesLeft -= framesToWrite;
365 		}
366 	}
367 
writeSineDataShort( BlockingStream stream, int framesPerBuffer, int numFrames )368 	private void writeSineDataShort( BlockingStream stream,
369 			int framesPerBuffer, int numFrames )
370 	{
371 		short[] buffer = new short[framesPerBuffer * 2];
372 		SineOscillator osc1 = new SineOscillator( 200.0, 44100 );
373 		SineOscillator osc2 = new SineOscillator( 300.0, 44100 );
374 		int framesLeft = numFrames;
375 		while( framesLeft > 0 )
376 		{
377 			int index = 0;
378 			int framesToWrite = (framesLeft > framesPerBuffer) ? framesPerBuffer
379 					: framesLeft;
380 			for( int j = 0; j < framesToWrite; j++ )
381 			{
382 				buffer[index++] = (short) (osc1.next() * 32767);
383 				buffer[index++] = (short) (osc2.next() * 32767);
384 			}
385 			stream.write( buffer, framesToWrite );
386 			framesLeft -= framesToWrite;
387 		}
388 	}
389 
testBlockingWriteShort()390 	public void testBlockingWriteShort()
391 	{
392 		PortAudio.initialize();
393 
394 		StreamParameters streamParameters = new StreamParameters();
395 		streamParameters.sampleFormat = PortAudio.FORMAT_INT_16;
396 		streamParameters.channelCount = 2;
397 		streamParameters.device = PortAudio.getDefaultOutputDevice();
398 		streamParameters.suggestedLatency = PortAudio
399 				.getDeviceInfo( streamParameters.device ).defaultLowOutputLatency;
400 		System.out.println( "suggestedLatency = "
401 				+ streamParameters.suggestedLatency );
402 
403 		int framesPerBuffer = 256;
404 		int flags = 0;
405 		BlockingStream stream = PortAudio.openStream( null, streamParameters,
406 				44100, framesPerBuffer, flags );
407 		assertTrue( "got default stream", stream != null );
408 
409 		int numFrames = 80000;
410 		stream.start();
411 		long startTime = System.currentTimeMillis();
412 		writeSineDataShort( stream, framesPerBuffer, numFrames );
413 		stream.stop();
414 		long stopTime = System.currentTimeMillis();
415 		stream.close();
416 
417 		double elapsed = (stopTime - startTime) / 1000.0;
418 		double expected = numFrames / 44100.0;
419 		assertEquals( "elapsed time to play", expected, elapsed, 0.20 );
420 		PortAudio.terminate();
421 	}
422 
testRecordPlayFloat()423 	public void testRecordPlayFloat() throws InterruptedException
424 	{
425 		checkRecordPlay( PortAudio.FORMAT_FLOAT_32 );
426 	}
427 
testRecordPlayShort()428 	public void testRecordPlayShort() throws InterruptedException
429 	{
430 		checkRecordPlay( PortAudio.FORMAT_INT_16 );
431 	}
432 
checkRecordPlay( int sampleFormat )433 	public void checkRecordPlay( int sampleFormat ) throws InterruptedException
434 	{
435 		int framesPerBuffer = 256;
436 		int flags = 0;
437 		int sampleRate = 44100;
438 		int numFrames = sampleRate * 3;
439 		float[] floatBuffer = null;
440 		short[] shortBuffer = null;
441 
442 		PortAudio.initialize();
443 		StreamParameters inParameters = new StreamParameters();
444 		inParameters.sampleFormat = sampleFormat;
445 		inParameters.device = PortAudio.getDefaultInputDevice();
446 
447 		DeviceInfo info = PortAudio.getDeviceInfo( inParameters.device );
448 		inParameters.channelCount = (info.maxInputChannels > 2) ? 2
449 				: info.maxInputChannels;
450 		System.out.println( "channelCount = " + inParameters.channelCount );
451 		inParameters.suggestedLatency = PortAudio
452 				.getDeviceInfo( inParameters.device ).defaultLowInputLatency;
453 
454 		if( sampleFormat == PortAudio.FORMAT_FLOAT_32 )
455 		{
456 			floatBuffer = new float[numFrames * inParameters.channelCount];
457 		}
458 		else if( sampleFormat == PortAudio.FORMAT_INT_16 )
459 		{
460 			shortBuffer = new short[numFrames * inParameters.channelCount];
461 		}
462 		// Record a few seconds of audio.
463 		BlockingStream inStream = PortAudio.openStream( inParameters, null,
464 				sampleRate, framesPerBuffer, flags );
465 
466 		System.out.println( "RECORDING - say something like testing 1,2,3..." );
467 		inStream.start();
468 
469 		if( sampleFormat == PortAudio.FORMAT_FLOAT_32 )
470 		{
471 			inStream.read( floatBuffer, numFrames );
472 		}
473 		else if( sampleFormat == PortAudio.FORMAT_INT_16 )
474 		{
475 			inStream.read( shortBuffer, numFrames );
476 		}
477 		Thread.sleep( 100 );
478 		int availableToRead = inStream.getReadAvailable();
479 		System.out.println( "availableToRead =  " + availableToRead );
480 		assertTrue( "getReadAvailable ", availableToRead > 0 );
481 
482 		inStream.stop();
483 		inStream.close();
484 		System.out.println( "Finished recording. Begin Playback." );
485 
486 		// Play back what we recorded.
487 		StreamParameters outParameters = new StreamParameters();
488 		outParameters.sampleFormat = sampleFormat;
489 		outParameters.channelCount = inParameters.channelCount;
490 		outParameters.device = PortAudio.getDefaultOutputDevice();
491 		outParameters.suggestedLatency = PortAudio
492 				.getDeviceInfo( outParameters.device ).defaultLowOutputLatency;
493 
494 		BlockingStream outStream = PortAudio.openStream( null, outParameters,
495 				sampleRate, framesPerBuffer, flags );
496 		assertTrue( "got default stream", outStream != null );
497 
498 		assertEquals( "inStream isActive", false, inStream.isActive() );
499 
500 		outStream.start();
501 		Thread.sleep( 100 );
502 		int availableToWrite = outStream.getWriteAvailable();
503 		System.out.println( "availableToWrite =  " + availableToWrite );
504 		assertTrue( "getWriteAvailable ", availableToWrite > 0 );
505 
506 		System.out.println( "inStream = " + inStream );
507 		System.out.println( "outStream = " + outStream );
508 		assertEquals( "inStream isActive", false, inStream.isActive() );
509 		assertEquals( "outStream isActive", true, outStream.isActive() );
510 		if( sampleFormat == PortAudio.FORMAT_FLOAT_32 )
511 		{
512 			outStream.write( floatBuffer, numFrames );
513 		}
514 		else if( sampleFormat == PortAudio.FORMAT_INT_16 )
515 		{
516 			outStream.write( shortBuffer, numFrames );
517 		}
518 		outStream.stop();
519 
520 		outStream.close();
521 		PortAudio.terminate();
522 	}
523 }
524