Skip to content

Instantly share code, notes, and snippets.

@mgodave
Created February 12, 2012 04:06
Show Gist options
  • Save mgodave/1806160 to your computer and use it in GitHub Desktop.
Save mgodave/1806160 to your computer and use it in GitHub Desktop.
portaudio paex_record.c java jna port
/**
* Created by IntelliJ IDEA.
* User: drusek
* Date: 2/10/12
* Time: 12:40 PM
* To change this template use File | Settings | File Templates.
*/
import com.sun.jna.*;
import com.sun.jna.ptr.PointerByReference;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.Arrays;
import java.util.concurrent.*;
/**
* Simple example of JNA interface mapping and usage.
*/
public class HelloWorld {
// This is the standard, stable way of mapping, which supports extensive
// customization and mapping of Java to native types.
public interface PortAudio extends Library {
PortAudio INSTANCE = (PortAudio)
Native.loadLibrary("portaudio", PortAudio.class);
public static final NativeLong paFloat32 = new NativeLong(0x00000001);
public static final NativeLong paInt16 = new NativeLong(0x00000008);
public static final NativeLong paClipOff = new NativeLong(0x00000001);
interface PaStreamCallback extends Callback {
public static enum PaStreamCallbackResult {
CONTINUE(0),
COMPLETE(1),
ABORT(2);
private final int value;
PaStreamCallbackResult(int value) {
this.value = value;
}
}
int invoke(Pointer inputBuffer, Pointer outputBuffer,
NativeLong frameCount,
Pointer timeInfo,
NativeLong statusFlags,
Pointer userData);
}
public static class PaStreamParameters extends Structure {
public int device;
public int channelCount;
public NativeLong paSampleFormat;
public double suggestedLatency;
public Pointer hostApiSpecificStreamInfo;
}
public static class PaDeviceInfo extends Structure {
public int structVersion;
public String name;
public int hostApi;
public int maxInputChannels;
public int maxOutputChannels;
double defaultLowInputLatency;
double defaultLowOutputLatency;
double defaultHighInputLatency;
double defaultHighOutputLatency;
double defauleSampleRate;
}
void Pa_Initialize();
void Pa_Terminate();
int Pa_OpenStream(PointerByReference stream,
PaStreamParameters inputParameters,
PaStreamParameters outputParameters,
double sampleRate,
NativeLong framesPerBuffer,
NativeLong streamFlags,
PaStreamCallback callback,
Pointer userData);
int Pa_StartStream(Pointer stream);
int Pa_CloseStream(Pointer stream);
int Pa_IsStreamActive(Pointer stream);
PaDeviceInfo Pa_GetDeviceInfo(int index);
String Pa_GetErrorText(int errorCode);
int Pa_GetDefaultInputDevice();
int Pa_GetDefaultOutputDevice();
void Pa_Sleep(int ms);
int Pa_WriteStream(Pointer stream, Pointer sampleBlock, NativeLong frames);
int Pa_ReadStream(Pointer stream, Pointer buffer, NativeLong frames);
}
public static class Capture implements Runnable {
public class RecordCallback implements PortAudio.PaStreamCallback {
private long frameIndex = 0;
private int maxFrameIndex = 0;
private FloatBuffer recordedSamples;
private final int numChannels;
public RecordCallback(int numSeconds, int sampleRate, int numChannels) {
maxFrameIndex = numSeconds * sampleRate;
recordedSamples = FloatBuffer.allocate(maxFrameIndex * numChannels);
this.numChannels = numChannels;
}
public FloatBuffer getRecordedSamples() {
return recordedSamples;
}
public int invoke(Pointer inputBuffer, Pointer outputBuffer, NativeLong frameCount, Pointer timeInfo, NativeLong statusFlags, Pointer userData) {
final long framesLeft = maxFrameIndex - frameIndex;
long framesToCalc;
PaStreamCallbackResult finished;
final FloatBuffer readBuffer = inputBuffer.getByteBuffer(0, frameCount.intValue() * numChannels * Float.SIZE).asFloatBuffer();
System.out.println("framesLeft: " + framesLeft + " frameCount: " + frameCount.longValue());
if (framesLeft < frameCount.longValue()) {
framesToCalc = framesLeft;
finished = PaStreamCallbackResult.COMPLETE;
} else {
framesToCalc = frameCount.longValue();
finished = PaStreamCallbackResult.CONTINUE;
}
if (inputBuffer == Pointer.NULL) {
for (int i = 0; i < framesToCalc; ++i) {
recordedSamples.put(0.0f);
if (numChannels == 2) {
recordedSamples.put(0);
}
}
} else {
for (int i = 0; i < framesToCalc; ++i) {
recordedSamples.put(readBuffer.get());
if (numChannels == 2) {
recordedSamples.put(readBuffer.get());
}
}
}
frameIndex += framesToCalc;
return finished.value;
}
}
public class PlaybackCallback implements PortAudio.PaStreamCallback {
private long frameIndex = 0;
private int maxFrameIndex = 0;
private final FloatBuffer recordedSamples;
private final int numChannels;
public PlaybackCallback(int numSeconds, int sampleRate, int numChannels, FloatBuffer recordedSamples) {
this.recordedSamples = recordedSamples;
maxFrameIndex = numSeconds * sampleRate;
this.numChannels = numChannels;
}
public int invoke(Pointer inputBuffer, Pointer outputBuffer, NativeLong frameCount, Pointer timeInfo, NativeLong statusFlags, Pointer userData) {
final long framesLeft = maxFrameIndex - frameIndex;
final FloatBuffer writeBuffer = outputBuffer.getByteBuffer(0, frameCount.intValue() * numChannels * Float.SIZE).asFloatBuffer();
PaStreamCallbackResult finished;
if (framesLeft < frameCount.longValue()) {
int i;
for (i = 0; i < framesLeft; ++i) {
writeBuffer.put(recordedSamples.get());
if (numChannels == 2) {
writeBuffer.put(recordedSamples.get());
}
}
for (; i < frameCount.longValue(); ++i) {
writeBuffer.put(0.0f);
if (numChannels == 2) {
writeBuffer.put(0.0f);
}
}
frameIndex += framesLeft;
finished = PaStreamCallbackResult.COMPLETE;
} else {
for (int i = 0; i < frameCount.longValue(); ++i) {
writeBuffer.put(recordedSamples.get());
if (numChannels == 2) {
writeBuffer.put(recordedSamples.get());
}
}
frameIndex += frameCount.longValue();
finished = PaStreamCallbackResult.CONTINUE;
}
return finished.value;
}
}
public static final int SAMPLE_RATE = 44100;
public static final int NUM_CHANNELS = 2;
public static final int NUM_SECONDS = 5;
public static final NativeLong FRAMES_PER_BUFFER = new NativeLong(512);
public static final NativeLong SAMPLE_TYPE = PortAudio.paFloat32;
public void run() {
final PortAudio.PaStreamParameters inputParameters = new PortAudio.PaStreamParameters();
inputParameters.device = PortAudio.INSTANCE.Pa_GetDefaultInputDevice();
inputParameters.channelCount = NUM_CHANNELS;
inputParameters.paSampleFormat = SAMPLE_TYPE;
inputParameters.hostApiSpecificStreamInfo = Pointer.NULL;
inputParameters.suggestedLatency = PortAudio.INSTANCE.Pa_GetDeviceInfo(inputParameters.device).defaultLowInputLatency;
final RecordCallback recordCallback = new RecordCallback(NUM_SECONDS, SAMPLE_RATE, NUM_CHANNELS);
final PointerByReference inputStreamReference = new PointerByReference();
int err = PortAudio.INSTANCE.Pa_OpenStream(
inputStreamReference,
inputParameters,
null,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
PortAudio.paClipOff,
recordCallback, null);
System.out.println(PortAudio.INSTANCE.Pa_GetErrorText(err));
err = PortAudio.INSTANCE.Pa_StartStream(inputStreamReference.getValue());
System.out.println(PortAudio.INSTANCE.Pa_GetErrorText(err));
while ((err = PortAudio.INSTANCE.Pa_IsStreamActive(inputStreamReference.getValue())) == 1) {
PortAudio.INSTANCE.Pa_Sleep(1000);
}
// try {
// final FileOutputStream outputStream = new FileOutputStream("/home/drusek/recorded.raw");
// final DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
// for (final float sample : recordCallback.getRecordedSamples().array()) {
// dataOutputStream.writeFloat(sample);
// }
// } catch (FileNotFoundException e) {
// e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
// } catch (IOException e) {
// e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
// }
err = PortAudio.INSTANCE.Pa_CloseStream(inputStreamReference.getValue());
System.out.println(PortAudio.INSTANCE.Pa_GetErrorText(err));
final PortAudio.PaStreamParameters outputParameters = new PortAudio.PaStreamParameters();
outputParameters.device = PortAudio.INSTANCE.Pa_GetDefaultOutputDevice();
outputParameters.channelCount = NUM_CHANNELS;
outputParameters.paSampleFormat = SAMPLE_TYPE;
outputParameters.hostApiSpecificStreamInfo = Pointer.NULL;
outputParameters.suggestedLatency = PortAudio.INSTANCE.Pa_GetDeviceInfo(outputParameters.device).defaultLowOutputLatency;
recordCallback.getRecordedSamples().rewind();
final PlaybackCallback playbackCallback = new PlaybackCallback(NUM_SECONDS, SAMPLE_RATE, NUM_CHANNELS, recordCallback.getRecordedSamples());
final PointerByReference outputStreamReference = new PointerByReference();
err = PortAudio.INSTANCE.Pa_OpenStream(
outputStreamReference,
null,
outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
PortAudio.paClipOff,
playbackCallback, null);
System.out.println(PortAudio.INSTANCE.Pa_GetErrorText(err));
err = PortAudio.INSTANCE.Pa_StartStream(outputStreamReference.getValue());
System.out.println(PortAudio.INSTANCE.Pa_GetErrorText(err));
while ((err = PortAudio.INSTANCE.Pa_IsStreamActive(outputStreamReference.getValue())) == 1) {
PortAudio.INSTANCE.Pa_Sleep(1000);
}
err = PortAudio.INSTANCE.Pa_CloseStream(outputStreamReference.getValue());
System.out.println(PortAudio.INSTANCE.Pa_GetErrorText(err));
}
}
public static void main(String[] args) throws ExecutionException {
PortAudio.INSTANCE.Pa_Initialize();
try {
final ExecutorService threadPool = Executors.newCachedThreadPool();
final Future captureFuture = threadPool.submit(new Capture());
captureFuture.get();
threadPool.shutdown();
threadPool.awaitTermination(10, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
PortAudio.INSTANCE.Pa_Terminate();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment