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