Am I doing something wrong here? (peer review)

Started by moci, February 13, 2012, 20:41:08

Previous topic - Next topic

moci

Hi, the goal is simple. Create a microphone streamer that has one source.
The stream can be delayed by some arbitrary amount (5 seconds in this case).

It works, but it's always worth having smarter people go over the code.

Here's the general class that has a main:
package capturetest;

public class MicStreamer {
	public static void main(String[] args) {
		InputStreamer inputStreamer = new InputStreamer();
		InputThread inputThread = new InputThread(inputStreamer);
		
		(new Thread(inputThread)).start();
		inputStreamer.execute();
	}
}


Here's the actual streaming class:
package capturetest;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.openal.AL;
import org.lwjgl.openal.AL10;
import org.lwjgl.openal.ALC10;
import org.lwjgl.openal.ALC11;
import org.lwjgl.openal.ALCdevice;

public class InputStreamer {
	public boolean doneCapturing = false;
	
	public void execute() {
		// Initialize LWJGL  OpenAL
		try {
			AL.create();
		} catch (LWJGLException le) {
			le.printStackTrace();
			return;
		}
		this.checkErrors();
		
		int FREQ = 44100;
		int FORMAT = AL10.AL_FORMAT_MONO16;
		int BUFFERSIZE = FREQ / 50;
		int BYTESPERSAMPLE = 16 / 8;
		
		// Connect to the input driver, it will allow us to capture sound
		ALCdevice inputDevice = ALC11.alcCaptureOpenDevice(null, FREQ, FORMAT, BUFFERSIZE);
		// Connect to the rapture3D driver, it will do the HRTF processing.
		ALCdevice outputDevice = ALC10.alcOpenDevice("Rapture3D");
		
		this.checkErrors();
		
		IntBuffer samplesCaptured = BufferUtils.createIntBuffer(1);
		ByteBuffer samplesBuffer = BufferUtils.createByteBuffer(BUFFERSIZE * BYTESPERSAMPLE);
		
		// Create a source object
		int source = AL10.alGenSources();
		
		// Add delay of X seconds
		int delayTime = 5;
		int delayBuffer = AL10.alGenBuffers();
		ByteBuffer delaySamples = BufferUtils.createByteBuffer(FREQ * BYTESPERSAMPLE * delayTime);
		
		AL10.alBufferData(delayBuffer, FORMAT, delaySamples, FREQ);
		AL10.alSourceQueueBuffers(source, delayBuffer);
		
		AL10.alDeleteBuffers(delayBuffer);
		
		this.checkErrors();
		
		while (!doneCapturing) {
			// Start capturing
			ALC11.alcCaptureStart(inputDevice);
			
			// Wait until the proper amount of samples has been captured
			while (samplesCaptured.get(0) < BUFFERSIZE) {
				ALC10.alcGetInteger(inputDevice, ALC11.ALC_CAPTURE_SAMPLES, samplesCaptured);
			}
			
			// Stop capturing
			ALC11.alcCaptureStop(inputDevice);
			
			// Retrieve the captured samples and set counter
			ALC11.alcCaptureSamples(inputDevice, samplesBuffer, BUFFERSIZE);
			ALC10.alcGetInteger(inputDevice, ALC11.ALC_CAPTURE_SAMPLES, samplesCaptured);
			
			this.checkErrors();
			
			// Create new buffer object
			int buffer = AL10.alGenBuffers();
			
			// Put samples data in buffer object
			AL10.alBufferData(buffer, FORMAT, samplesBuffer, FREQ);
			
			// Queue buffer to the source object
			AL10.alSourceQueueBuffers(source, buffer);
			
			int sourceState = AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE);
			if (sourceState != AL10.AL_PLAYING) {
				AL10.alSourcePlay(source);
			}
			
			// Destroy buffer
			AL10.alDeleteBuffers(buffer);
			
			// Dequeue processed buffers
			AL10.alSourceUnqueueBuffers(source);
			
			this.checkErrors();
		}
		
		// Close the in/out device
		ALC10.alcCloseDevice(inputDevice);
		ALC10.alcCloseDevice(outputDevice);
		
		// Destroy source object
		AL10.alDeleteSources(source);
		
		// Close OpenAL
		AL.destroy();
	}
	
	/**
	 * Check the current error state of AL
	 * @return true if there was an error. false if 'AL_NO_ERROR' (no error).
	 */
	private boolean checkOpenALErrors() {
		boolean ret = true;

		switch (AL10.alGetError()) {
		case AL10.AL_NO_ERROR:
			ret = false;
			break;
		case AL10.AL_INVALID_NAME:
			System.out.println("Invalid name parameter.");
			break;
		case AL10.AL_INVALID_ENUM:
			System.out.println("Invalid parameter.");
			break;
		case AL10.AL_INVALID_VALUE:
			System.out.println("Invalid enum parameter value.");
			break;
		case AL10.AL_INVALID_OPERATION:
			System.out.println("Illegal call.");
			break;
		case AL10.AL_OUT_OF_MEMORY:
			System.out.println("Unable to allocate memory.");
		}
		
		if (ret) {
			System.out.println("- Error in AL.");
		}

		return ret;
	}
	
	/**
	 * Check the current error state of ALC
	 * @return true if there was an error. false if 'ALC_NO_ERROR' (no error).
	 */
	private boolean checkOpenALCErrors() {
		boolean ret = true;

		switch (AL10.alGetError()) {
		case ALC10.ALC_NO_ERROR:
			ret = false;
			break;
		case ALC10.ALC_INVALID_DEVICE:
			System.out.println("The device handle or specifiers names an inaccessible driver/server");
			break;
		case ALC10.ALC_INVALID_CONTEXT:
			System.out.println("The Context argument does not name a valid context.");
			break;
		case ALC10.ALC_INVALID_ENUM:
			System.out.println("A token used is not valid, or not applicable.");
			break;
		case ALC10.ALC_INVALID_VALUE:
			System.out.println("A value (e.g. Attribute is not valid, or not applicable.");
			break;
		case ALC10.ALC_OUT_OF_MEMORY:
			System.out.println("Unable to allocate memory.");
			break;
		}
		
		if (ret) {
			System.out.println("- Error in ALC.");
		}

		return ret;
	}
	
	private boolean checkErrors() {
		return this.checkOpenALErrors() || this.checkOpenALCErrors();
	}
}


Here is the input request class (for stopping the application) - a dirty copy/paste from OpenAL_lesson1 from the wiki:
package capturetest;

import java.util.Scanner;

public class InputThread implements Runnable {
	private InputStreamer _streamer;
	
	public InputThread(InputStreamer streamer) {
		_streamer = streamer;
	}
	
	public void run() {
	    char c = ' ';
	    Scanner stdin = new Scanner(System.in);
	    while(c != 'q') {
	    	try {
    			System.out.print("Input (q to stop): ");
    			c = (char)stdin.nextLine().charAt(0);
	    	} catch (Exception ex) {
	    		c = 'q';
	    	}
	    	
	    	if (c == 'q') {
	    		_streamer.doneCapturing = true;
	    	}
	    }
	}
}


If you would run this code, mind that I'm using "Rapture3D" as an output device, you might not have it installed and must replace it with a "null" to select a default device. I'm using LWJGL 2.8.3 .

Thanks