I have three classes that I am working with.
The first class opens the GPS and reads the data.
The second parses the data.
The third is going to be the parsed data in a GUI.
My question is how do I pass the global variable gpsData to classC from NMEA?
I am using code that I found online for the gps data.
It looks like the NMEA class is dependent on serialGps and serialGps is dependent on NMEA.
I need to pass the inputLine which sets the global variable "gpsData" contained in the NMEA class to a third class which will be my GUI.
This is the serialGps class
package gpsData;
import com.fazecast.jSerialComm.SerialPort;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
#SuppressWarnings({"unused", "WeakerAccess"})
public class SerialGps {
public interface StateListener {
void onGpsStateUpdated(NMEA.GpsState state);
}
private String portName;
private int baudRate;
private boolean isRunning = false;
private List<StateListener> stateListeners = new ArrayList<>();
public SerialGps(String portName, int baudRate) {
this.portName = portName;
this.baudRate = baudRate;
}
public SerialGps(String portName) {
this(portName, 4800);
}
public void addStateListener(StateListener stateListener) {
stateListeners.add(stateListener);
}
public void start() {
NMEA nmea = new NMEA();
SerialPort[] serialPorts = SerialPort.getCommPorts();
SerialPort gpsPort = null;
for (SerialPort serialPort : serialPorts) {
if (serialPort.getDescriptivePortName().toLowerCase().contains("serial")) {
gpsPort = serialPort;
}
}
if (gpsPort == null) {
System.out.println("failed to find gps serial port");
return;
}
System.out.println("using serial port: " + gpsPort.getDescriptivePortName());
gpsPort.setBaudRate(4800);
gpsPort.openPort();
InputStream inStream = gpsPort.getInputStream();
if (inStream == null) {
System.out.println("opening port " + gpsPort.getDescriptivePortName() + " failed");
return;
}
Thread thread = new Thread(() -> {
String inputLine = "";
isRunning = true;
while (isRunning) {
try {
if (inStream.available() > 0) {
char b = (char) inStream.read();
if (b == '\n') {
NMEA.GpsState gpsState = nmea.getUpdatedStatus(inputLine);
updateState(gpsState);
inputLine = "";
} else {
inputLine += b;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.start();
}
public void stop() throws InterruptedException {
isRunning = false;
}
private void updateState(NMEA.GpsState gpsState) {
stateListeners.forEach(stateListener -> stateListener.onGpsStateUpdated(gpsState));
}
public static void main(String[] args) {
// TODO Auto-generated method stub
SerialGps app = new SerialGps("/dev/cu.usbserial", 4800);
app.start();
}
}
The NMEA class:
package gpsData;
import java.io.IOException;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
public class NMEA {
public static String gpsData = null;
interface SentenceParser {
boolean parse(String[] tokens, GpsState position);
}
private class GPGGA implements SentenceParser {
public boolean parse(String[] tokens, GpsState position) {
position.time = Float.parseFloat(tokens[1]);
position.lat = Latitude2Decimal(tokens[2], tokens[3]);
position.lon = Longitude2Decimal(tokens[4], tokens[5]);
position.quality = Integer.parseInt(tokens[6]);
position.altitude = Float.parseFloat(tokens[9]);
return true;
}
}
private class GPGGL implements SentenceParser {
public boolean parse(String[] tokens, GpsState position) {
position.lat = Latitude2Decimal(tokens[1], tokens[2]);
position.lon = Longitude2Decimal(tokens[3], tokens[4]);
position.time = Float.parseFloat(tokens[5]);
return true;
}
}
private class GPRMC implements SentenceParser {
public boolean parse(String[] tokens, GpsState position) {
position.time = Float.parseFloat(tokens[1]);
position.lat = Latitude2Decimal(tokens[3], tokens[4]);
position.lon = Longitude2Decimal(tokens[5], tokens[6]);
position.velocity = Float.parseFloat(tokens[7]);
position.dir = Float.parseFloat(tokens[8]);
return true;
}
}
private class GPVTG implements SentenceParser {
public boolean parse(String[] tokens, GpsState position) {
position.dir = Float.parseFloat(tokens[3]);
return true;
}
}
private class GPRMZ implements SentenceParser {
public boolean parse(String[] tokens, GpsState position) {
position.altitude = Float.parseFloat(tokens[1]);
return true;
}
}
private static float Latitude2Decimal(String lat, String NS) {
float result = (Float.parseFloat(lat.substring(2)) / 60.0f) + Float.parseFloat(lat.substring(0, 2));
if (NS.startsWith("S")) {
result *= -1.0f;
}
return result;
}
private static float Longitude2Decimal(String lon, String WE) {
float med = (Float.parseFloat(lon.substring(3)) / 60.0f) + Float.parseFloat(lon.substring(0, 3));
if (WE.startsWith("W")) {
med *= -1.0f;
}
return med;
}
#SuppressWarnings("WeakerAccess")
public class GpsState {
public float time = 0.0f;
public float lat = 0.0f;
public float lon = 0.0f;
public boolean hasFix = false;
public int quality = 0;
public float dir = 0.0f;
public float altitude = 0.0f;
public float velocity = 0.0f;
void updatefix() {
hasFix = quality > 0;
}
public String toString() {
return String.format("POSITION: lat: %f, lon: %f, time: %f, Q: %d, dir: %f, alt: %f, vel: %f", lat, lon, time, quality, dir, altitude, velocity);
}
}
private GpsState position = new GpsState();
private static final Map<String, SentenceParser> sentenceParsers = new HashMap<>();
NMEA() {
sentenceParsers.put("GPGGA", new GPGGA());
sentenceParsers.put("GPGGL", new GPGGL());
sentenceParsers.put("GPRMC", new GPRMC());
sentenceParsers.put("GPRMZ", new GPRMZ());
sentenceParsers.put("GPVTG", new GPVTG());
}
GpsState getUpdatedStatus(String inputLine) {
if (inputLine.startsWith("$")) {
//System.out.println(inputLine);
String nmea = inputLine.substring(1);
String[] tokens = nmea.split(",");
String type = tokens[0];
this.gpsData = inputLine;
//System.out.println(gpsData);
if (sentenceParsers.containsKey(type)) {
try {
sentenceParsers.get(type).parse(tokens, position);
} catch (Exception e) {}
}
position.updatefix();
//System.out.println(gpsData);
}
return position;
}
public static String passer() {
String gpsData2 = gpsData;
return gpsData2;
}
}
classC
package gpsData;
public class classC {
static String inputLinet = null;
public void print() {
inputLinet = NMEA.gpsData;
if (inputLinet.startsWith("$")) {
System.out.print(inputLinet);
}
}
}
Related
I am programming a little drum sequencer, a roland tr808 knockoff with 16 steps/measure and 16 instruments(=drum samples). User has a gui where he can thus create a 16x16 pattern.
However, if a sample is played more than once in quick succession, it often just gets played once. Say, I got a bassdrum on step 1, 5, 9 and 13 and tempo's 130BPM, it sometimes plays just the bd on 1 and 9, and sometimes the ones on 5 and/or 13 as well. If the sample is very short or the tempo is slow, the chances are higher that every step in the pattern is played correctly. So I assume that the audio line doesn't like it when I try to play a sample again when it hasn't finished yet.
But actually I thought I'd taken that into account in my code. I'd be really thankful if someone told me what's wrong with my code.
Here's my complete code as suggested by Andrew Thompson, modified so that it takes some samples from the internet. Loading them takes a bit, though. the part causing the issue is probably the play() method in the Instrument class:
package testbox;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.sound.sampled.*;
public class boomboxtest {
public static void main(String[] args) {
Sequencer seq = new Sequencer();
//bassdrum
seq.toggleInstrument(0,0);
seq.toggleInstrument(0,4);
seq.toggleInstrument(0,8);
seq.toggleInstrument(0,12);
//snare
seq.toggleInstrument(1,4);
seq.toggleInstrument(1,12);
//Hihat
seq.toggleInstrument(2, 2);
seq.toggleInstrument(2, 6);
seq.toggleInstrument(2, 10);
//Bongo
seq.toggleInstrument(3, 6);
seq.toggleInstrument(3, 10);
seq.setTempo(130);
seq.play();
}
}
class Sequencer {
private Mixer mixer;
private List<SequencerListener> listeners = new ArrayList<SequencerListener>();
public static final int INSTR_COUNT = 4;
private int tempo_bpm = 120;
private ExecutorService executor;
private int current_step = 0;
private int current_max_step = 16;
private boolean[][] pattern = new boolean[32][INSTR_COUNT];
private ArrayList<Instrument> instruments;
Line[] lines = new Line[16];
private SequencerEngine seq;
private String[] filenames = {"http://www.canadianmusicartists.com/sample/kick_02.wav", "http://www.canadianmusicartists.com/sample/snare01.wav", "http://www.canadianmusicartists.com/sample/H_closedhat_01.wav", "http://www.canadianmusicartists.com/sample/bongo01.wav"};
public Sequencer() {
seq = new SequencerEngine();
try{
Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo();
mixer = AudioSystem.getMixer(mixerInfo[0]);
} catch (Exception e) {e.printStackTrace();}
instruments = new ArrayList<Instrument>(INSTR_COUNT);
for (int i = 0; i < INSTR_COUNT; i++) {
System.out.println("Loading instrument " + i);
Instrument instr = new Instrument(filenames[i], mixer);
instruments.add(instr);
lines[i] = instr.getLine();
}
syncMixer();
executor = Executors.newCachedThreadPool();
executor.submit(seq);
}
public void syncMixer() {
if (mixer.isSynchronizationSupported(lines, false)) {
mixer.synchronize(lines, false);
} else {
System.out.println("No hay synchronisado");
}
}
public boolean isPlaying() {
return seq.getRunning();
}
public boolean toggleInstrument (int instrument, int beat) {
pattern[beat][instrument] = !pattern[beat][instrument];
return pattern[beat][instrument];
}
public void play() {
seq.toggleRun(true);
}
public void pause() {
seq.toggleRun(false);
}
public void stop() {
pause();
setCurrent_step(0);
}
public int getTempo() {
return tempo_bpm;
}
public void setTempo(int tempo) {
if (tempo < 30) {
tempo = 30;
} else if (tempo > 200) {
tempo = 200;
} else {
this.tempo_bpm = tempo;
}
}
public int getCurrent_step() {
return current_step;
}
public void setCurrent_step(int current_step) {
this.current_step = current_step;
}
public boolean[][] getPattern() {
return pattern;
}
public void kill() {
seq.kill();
executor.shutdownNow();
}
public void addListener(SequencerListener toAdd) {
listeners.add(toAdd);
}
public class SequencerEngine implements Runnable{
private boolean running;
private boolean alive = true;
public void run() {
while( getAlive()) {
while (getRunning()) {
if (current_step >= current_max_step) {
current_step = 0;
}
for (; current_step < current_max_step ; current_step++) {
stepListen();
if(!getRunning()) {
break;
}
long time = System.currentTimeMillis();
long steptime = 60000/(4*tempo_bpm);
for (int k = 0; k < INSTR_COUNT; k++) {
if (pattern[current_step][k]) {
instruments.get(k).play();
}
}
while((System.currentTimeMillis()-time) < steptime) {}
}
}
}
}
public void stepListen() {
for (SequencerListener sl : listeners) {
sl.stepEvent(current_step);
}
}
public boolean getRunning() {
return running;
}
public boolean getAlive() {
return alive;
}
public void toggleRun(boolean toggle) {
running = toggle;
}
public void kill() {
alive = false;
}
}
}
class Instrument {
private String name;
private File soundFile;
private AudioInputStream stream;
private AudioFormat format;
private DataLine.Info info;
private Clip clip;
private Mixer mixer;
public Instrument(String filename, Mixer mixer ) {
this.name = filename;
try {
//soundFile = new File("sounds/" + filename);
URL url = new URL(filename);
this.mixer = mixer;
//stream = AudioSystem.getAudioInputStream(soundFile);
stream = AudioSystem.getAudioInputStream(url);
format = stream.getFormat();
info = new DataLine.Info(Clip.class, format);
clip = (Clip) mixer.getLine(info);
clip.open(stream);
}
catch (Exception e) {
e.printStackTrace();
}
}
public void play() {
clip.stop();
clip.setFramePosition(0);
clip.start();
}
public Line getLine() {
return clip;
}
}
interface SequencerListener {
void stepEvent(int current_step);
}
The samples are of rather questionable quality, but especially the bassdrum sample illustrates my problem really good.
We are making a six-player multi-player game for school and I am responsible for a networking class. The game incorporates Slick 2D. In the game there are entities (code shown below)
package Game;
import org.newdawn.slick.*;
public abstract class Entity {
double maxHealth, health, speed, damage, width, height, locationX, locationY;
Animation standing, walkingLeft, walkingRight, attacking, jumping, current;
long pastTime = 0;
public boolean isReadyToAttack(long delta) {
if(pastTime < 2 * 500) { //multiply by 1000 to get milliseconds
pastTime += delta;
return false;
}else{
pastTime = 0;
return true;
}
}
public void setMaxHealth(double d){
maxHealth = d;
}
public void setHealth(double d){
health = d;
}
public void setSpeed(double x){
speed = x;
}
public void setDamage(double x){
damage = x;
}
public void setLocation(double x, double y){
locationX = x;
locationY = y;
}
public void setSprites(Animation s, Animation r){
standing = s;
walkingRight = r;
}
public void setCurrent(Animation a){
current = a;
}
public double getMaxHealth(){
return maxHealth;
}
public double getHealth(){
return health;
}
public double getSpeed(){
return speed;
}
public double getDamage(){
return damage;
}
public double getLocationX(){
return locationX;
}
public double getLocationY(){
return locationY;
}
public Animation getStanding(){
return standing;
}
public Animation getRight(){
return walkingRight;
}
public Animation getCurrent(){
return current;
}
public abstract double getAttackRange();
}
The entities class is also extended by the Heroes class which is further extended by the Ares class. Ares is one of the six characters in our game and when you start the game you can choose one of the six in the start menu.
Here is the Hero class:
package Game;
import org.newdawn.slick.Animation;
public abstract class Hero extends Entity{
static double gravity = 0.1;
double velocity;
int level;
boolean disabled, grounded = true;
public void setLevel(int i){
level = i;
}
public void setVelocity(double d){
velocity = d;
}
public void setDisabled(boolean b){
disabled = b;
}
public void setGrounded(boolean b){
grounded = b;
}
public int getLevel(){
return level;
}
public double getVelocity(){
return velocity;
}
public boolean getDisabled(){
return disabled;
}
public boolean getGrounded(){
return grounded;
}
public double getGravity(){
return gravity;
}
public boolean isAlive(){
if(getHealth() > 0)
return true;
return false;
}
public void attack(Entity gettingAttacked, Hero attacking) {
gettingAttacked.setHealth(gettingAttacked.getHealth() - attacking.getDamage());
}
public boolean isReadyToRegenerate(long delta) {
if(pastTime < 1 * 1000){
pastTime += delta;
return false;
}
else{
pastTime = 0;
return true;
}
}
public static void regenerate(Hero h, long delta){
if(h.getHealth() >= 0 && h.getHealth() < h.getMaxHealth())
if(h.isReadyToRegenerate(delta))
h.setHealth(h.getHealth() + (h.getMaxHealth()) * 0.01);
}
public abstract void levelUp();
}
Here is the Ares class:
package Game;
import org.newdawn.slick.Animation;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.SpriteSheet;
public class Ares extends Hero {
public Ares() throws SlickException {
setHealth(500);
setSpeed(0.4);
setDamage(70);
setLocation(850,625);
setSprites(new Animation(new SpriteSheet("res/Ares.png", 191, 275), 200), new Animation(new SpriteSheet("res/AresWalk.png", 191, 275), 200));
}
public void levelUp() {
level += 1;
health *= 1.1;
damage *= 1.1;
}
public double getAttackRange() {
return 250;
}
public double maxHealth() {
return 500;
}
}
Here is the Battle code, which contains the main gameplay code, the code is currently uncompleted and only has movement:
package Game;
import java.util.ArrayList;
import org.lwjgl.input.*;
import org.newdawn.slick.*;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.state.*;
public class Battle extends BasicGameState {
private ArrayList<Entity> entities = new ArrayList<Entity>();
private Hero player;
private Image background;
private int midScreen, ground;
public void init(GameContainer gc, StateBasedGame sbg) throws SlickException{
gc.setVSync(true);
player = new Ares();
player.setCurrent(player.getStanding());
entities.add(player);
background = new Image("res/Background.png");
midScreen = 800;
ground = 625;
}
public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException{
g.translate((float)-player.getLocationX() + midScreen, (float)-player.getLocationY() + ground);
background.draw(0, -920);
for(int i = 0; i < entities.size(); i++)
entities.get(i).getCurrent().draw((float)entities.get(i).getLocationX(), (float)entities.get(i).getLocationY());
}
public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
player.getCurrent().update(delta); // ensures
Input kb = gc.getInput();
if (kb.isKeyDown(Input.KEY_SPACE) && player.getGrounded()) {
player.setGrounded(false);
player.setVelocity(7);
player.setLocation(player.getLocationX(), player.getLocationY() - player.getVelocity());
player.setCurrent(player.getStanding());
}
else if (kb.isKeyDown(Input.KEY_A) && player.getLocationX() > 800) {
player.setLocation(player.getLocationX() - player.getSpeed() * delta, player.getLocationY());
if(player.grounded)
player.setCurrent(player.getRight());
}
else if (kb.isKeyDown(Input.KEY_D) && player.getLocationX() < 8580) {
player.setLocation(player.getLocationX() + player.getSpeed() * delta, player.getLocationY());
if(player.grounded)
player.setCurrent(player.getRight());
}
else
player.setCurrent(player.getStanding());
if (Math.abs(player.getLocationY() - ground) < .01)
player.setGrounded(true);
if (player.getGrounded() == false) {
player.setVelocity(player.getVelocity() - player.getGravity());
player.setLocation(player.getLocationX(), player.getLocationY() - player.getVelocity());
}
//else if (kb.isKeyDown(Input.KEY_A)) //&& (!(aresRect.intersects(turretRect))))
//{
// x -= 0.3 * delta;
//}
//if(kb.isKeyDown(Input.KEY_S))
// y += 0.3 * delta;
//else if (kb.isKeyDown(Input.KEY_W)) //&& (!(aresRect.intersects(turretRect))))
// y -= 0.3 * delta;
}
public int getID(){
return 1;
}
}
What I am having difficulty with is creating a server and client that gets the input from the players, such as location, who chose which player, etc. I'm wondering that if in Battle, when Player objects are made, how would I be able to get location and other specific attributes of the objects and give them to the other computers?
So far, the furthest I have gotten with networking code is a chat server, but I am confused as to how to apply parts of that information into the game to include getting input from a user. I have looked at a bunch of tutorials on creating a multi-player network, but I'm still confused. As for my experience, I've had around 2 years experience coding (only one with Java) but this is my first time working with networking. I experimented with a UDP connection, here is my Server:
package net;
import java.io.*;
import java.net.*;
import java.util.*;
import main.Game;
import packets.*;
import packets.Packet.PacketTypes;
public class GameServer extends Thread{
private DatagramSocket socket;
private Game game;
private List<PlayerMP> connectedPlayers = new ArrayList <PlayerMP>();
public GameServer(Game game){
this.game = game;
try {
socket = new DatagramSocket(7777);
} catch (SocketException e) {
e.printStackTrace();
}
}
public void run(){
while(true){
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
socket.receive(packet);
} catch(IOException e) {
e.printStackTrace();
}
parsePacket(packet.getData(), packet.getAddress(), packet.getPort());
}
}
private void parsePacket(byte[] data, InetAddress address, int port) {
String message = new String(data).trim();
PacketTypes type = Packet.lookupPacket(message.substring(0, 2));
Packet packet = null;
switch(type) {
default :
case INVALID:
break;
case LOGIN:
packet = new Packet00Login(data);
System.out.println("[" + address.getHostAddress() + ":" + port + "] " + ((Packet00Login) packet).getUsername() +
" has connected.");
PlayerMP player = new PlayerMP(address, port);
this.addConnection(player, ((Packet00Login)packet));
this.connectedPlayers.add(player);
break;
case DISCONNECT:
break;
case MOVE:
packet = new Packet02Move(data);
System.out.println(((Packet02Move)packet).getName() +"has moved to" + ((Packet02Move)packet).getX() + "," + ((Packet02Move)packet).getY());
this.handleMove((Packet02Move)packet);
}
}
private void handleMove(Packet02Move packet) {
if (getPlayerMP(packet.getUsername()) != null){
int index = getPlayerMPIndex(getName());
this.connectedPlayers.get(index).locationY = packet.getX();
this.connectedPlayers.get(index).locationX = packet.getX();
packet.writeData(this);
}
}
public void addConnection(PlayerMP player, Packet00Login packet) {
boolean alreadyConnected = false;
for(PlayerMP p : this.connectedPlayers) {
if (player.getUsername().equalsIgnoreCase(p.getUsername())) {
if(p.ipAddress == null){
p.ipAddress = player.ipAddress;
}
if(p.port == -1) {
p.port = player.port;
}
alreadyConnected = true;
} else {
sendData(packet.getData(), p.ipAddress, p.port);
packet = new Packet00Login(p.getUsername());
sendData(packet.getData(), player.ipAddress, player.port);
}
if (alreadyConnected){
this.connectedPlayers.add(player);
packet.writeData(this);
}
}
}
public void sendData(byte[] data, InetAddress ipAddress, int port) {
DatagramPacket packet = new DatagramPacket(data, data.length, ipAddress, port);
try {
socket.send(packet);
} catch(IOException e) {
e.printStackTrace();
}
}
public void sendDataToAllClients(byte[] data) {
for(PlayerMP p : connectedPlayers)
sendData(data, p.ipAddress, p.port);
}
}
I could not figure out how to get much further than sending a message back and forth with a TCP connection:
import java.io.*;
import java.net.*;
public class Server {
private static ServerSocket serverSocket;
private static Socket socket;
private static DataOutputStream out;
private static Users[] user = new Users[6];
private static DataInputStream in;
public static void main(String[] args) throws Exception{
System.out.println("Starting server");
serverSocket = new ServerSocket(0);
System.out.println("Server Started");
while(true){
socket = serverSocket.accept();
for (int i = 0; i < 6; i++){
System.out.println("Connection from: " + socket.getInetAddress());
out = new DataOutputStream(socket.getOutputStream());
in = new DataInputStream(socket.getInputStream());
if (user[i] == null){
user[i] = new Users(out, in, user);
Thread thread = new Thread();
thread.start();
break;
}
}
}
}
}
class Users implements Runnable{
private DataOutputStream out;
private DataInputStream in;
private Users[] user = new Users[6];
public Users(DataOutputStream fromOut, DataInputStream goingIn, Users[] userArr){
out = fromOut;
in = goingIn;
user = userArr;
}
public void run(){
while(true){
try{
String message = in.readUTF();
for (int i = 0; i < 6; i++){
if (user[i] != null)
user[i].out.writeUTF(message);
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
I would like to figure this out. I am not even completely sure where to start or whether or not I am on the right track.
Note: I am coding in Java, and trying to use a TCP connection to code this. I am open to UDP if you think it may be easier.
Im pretty new to Java and to thread-programming especially. This code is mostly out of a pretty old book (2001) with samples and examples for a search-engine.
But its just not working
Now i don't know if i am making a mistake or if the author made it or if there are incompatibilities with different versions of java...i really have no clue! The oddest thing about it is that it works 1 out of 100 times ...
After hours of debugging i would appreciate any help!
SearchEngine.java:
import java.util.Vector;
import parsing.SourceElement;
import parsing.WebParserWrapper;
import query.Filter;
public class SearchEngine implements Runnable {
private Vector linkHistory = new Vector();
private int currentLink;
private String beginAt = null;
private SearchHandler searchHandler = null;
private boolean searchInProgress = false;
private boolean stopPending = false;
boolean firstTime = true;
public boolean searchInProgress() {
return searchInProgress;
}
public boolean stopPending() {
return stopPending;
}
#SuppressWarnings("unchecked")
public void followLinks(String url) {
if (stopPending)
return;
try {
boolean drillDown = false;
WebParserWrapper webParser = new WebParserWrapper();
Vector sortedElements = webParser.getElements(url, "", "WITHGET");
Vector contentElements = Filter.getFilteredElements(sortedElements, Filter.CONTENT, "matches", "*");
for (int i = 0; i < contentElements.size(); i++) {
SourceElement thisElement = (SourceElement) contentElements.elementAt(i);
String thisKey = (String) thisElement.getKey();
String thisContent = (String) thisElement.getContent();
boolean goodHit = searchHandler.handleElement(url, thisKey, thisContent);
if (goodHit) {
drillDown = true;
}
}
System.out.println(url + " -- DrillDown " + ((drillDown) ? "positive" : "negative"));
if (drillDown) {
Vector linkElements = Filter.getFilteredElements(sortedElements, Filter.KEY, "matches",
"*a[*].#href[*]");
for (int i = 0; i < linkElements.size(); i++) {
SourceElement thisElement = (SourceElement) linkElements.elementAt(i);
String thisContent = (String) thisElement.getContent();
if (!linkHistory.contains(thisContent)) {
linkHistory.add(thisContent);
System.out.println("Collected: " + thisContent);
}
}
}
}
catch (Exception e) {}
if (currentLink < linkHistory.size()) {
String nextLink = (String) linkHistory.elementAt(currentLink++);
if (nextLink != null) {
followLinks(nextLink);
}
}
}
public boolean startSearch(String url, SearchHandler searchHandler) {
if (searchInProgress)
return false;
beginAt = url;
this.searchHandler = searchHandler;
this.linkHistory = new Vector();
this.currentLink = 0;
Thread searchThread = new Thread(this);
searchThread.start();
return true;
}
public void stopSearch() {
stopPending = true;
}
#Override
public void run() {
searchInProgress = true;
followLinks(beginAt);
searchInProgress = false;
stopPending = false;
}
}
SimpleSearcher.java
import java.util.Enumeration;
import java.util.Hashtable;
public class SimpleSearcher implements SearchHandler {
private SearchEngine searchEngine;
private String keyword;
private String startURL;
private Hashtable hits = new Hashtable();
public boolean handleElement(String url, String key, String content) {
boolean goodHit = false;
int keywordCount = 0;
int pos = -1;
while ((pos = content.toLowerCase().indexOf(keyword, pos + 1)) >= 0){
keywordCount++;
}
if (keywordCount > 0) {
Integer count = (Integer) hits.get(url);
if (count == null){
hits.put(url, new Integer(1));
}
else {
hits.remove(url);
hits.put(url, new Integer(count.intValue() + keywordCount));
}
goodHit = true;
}
if (hits.size() >= 3)
searchEngine.stopSearch();
return goodHit;
}
public Hashtable search(String startURL, String keyword) {
searchEngine = new SearchEngine();
this.startURL = startURL;
this.keyword = keyword;
searchEngine.startSearch(startURL, this);
try {Thread.sleep(1000);}catch (Exception e){e.printStackTrace();}
while (searchEngine.searchInProgress());
return this.hits;
}
public static void main(String[] args) {
SimpleSearcher searcher = new SimpleSearcher();
String url = "http://www.nzz.ch/";
String compareWord = "der";
Hashtable hits = searcher.search(url, compareWord);
System.out.println("URLs=" + hits.size());
for (Enumeration keys = hits.keys(); keys.hasMoreElements();) {
String thisKey = (String) keys.nextElement();
int thisCount = ((Integer) hits.get(thisKey)).intValue();
System.out.println(thisCount + " hits at " + thisKey);
}
}
}
SearchHandler.java
public interface SearchHandler {
public boolean handleElement(String url, String key, String content);
}
I'm trying to implement iOS audio recording using RoboVM using the Apple's AudioQueue guide and their sample SpeakHere project
and am running into this error:
No #Marshaler found for parameter 1 of #Callback method <AQRecorder: void HandleInputBuffer(AQRecorder,org.robovm.apple.audiotoolbox.AudioQueue,org.robovm.apple.audiotoolbox.AudioQueueBuffer,org.robovm.apple.coreaudio.AudioTimeStamp,int,org.robovm.apple.coreaudio.AudioStreamPacketDescription)>
Any ideas? Here's the code I'm using:
Main.java:
import org.robovm.apple.coregraphics.CGRect;
import org.robovm.apple.foundation.NSAutoreleasePool;
import org.robovm.apple.uikit.UIApplication;
import org.robovm.apple.uikit.UIApplicationDelegateAdapter;
import org.robovm.apple.uikit.UIApplicationLaunchOptions;
import org.robovm.apple.uikit.UIButton;
import org.robovm.apple.uikit.UIButtonType;
import org.robovm.apple.uikit.UIColor;
import org.robovm.apple.uikit.UIControl;
import org.robovm.apple.uikit.UIControlState;
import org.robovm.apple.uikit.UIEvent;
import org.robovm.apple.uikit.UIScreen;
import org.robovm.apple.uikit.UIWindow;
public class IOSDemo extends UIApplicationDelegateAdapter {
private UIWindow window = null;
#Override
public boolean didFinishLaunching(UIApplication application,
UIApplicationLaunchOptions launchOptions) {
final AQRecorder aqRecorder = new AQRecorder();
final UIButton button = UIButton.create(UIButtonType.RoundedRect);
button.setFrame(new CGRect(115.0f, 121.0f, 91.0f, 37.0f));
button.setTitle("Start", UIControlState.Normal);
button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener() {
#Override
public void onTouchUpInside(UIControl control, UIEvent event) {
if(button.getTitle(UIControlState.Normal) == "Stop"){
aqRecorder.stopRecord();
button.setTitle("Start", UIControlState.Normal);
}
else{
aqRecorder.startRecord();
button.setTitle("Stop", UIControlState.Normal);
}
}
});
window = new UIWindow(UIScreen.getMainScreen().getBounds());
window.setBackgroundColor(UIColor.lightGray());
window.addSubview(button);
window.makeKeyAndVisible();
try {
aqRecorder.setUpAudioFormat();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
public static void main(String[] args) {
try (NSAutoreleasePool pool = new NSAutoreleasePool()) {
UIApplication.main(args, null, IOSDemo.class);
}
}
}
AQRecorder.java:
import org.robovm.apple.audiotoolbox.AudioFile;
import org.robovm.apple.audiotoolbox.AudioQueue;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer;
import org.robovm.apple.audiotoolbox.AudioQueue.AudioQueuePtr;
import org.robovm.apple.coreaudio.AudioFormat;
import org.robovm.apple.coreaudio.AudioStreamBasicDescription;
import org.robovm.apple.coreaudio.AudioStreamPacketDescription;
import org.robovm.apple.coreaudio.AudioTimeStamp;
import org.robovm.rt.bro.annotation.Callback;
import org.robovm.rt.bro.ptr.FunctionPtr;
import org.robovm.rt.bro.ptr.VoidPtr;
public class AQRecorder {
AudioStreamBasicDescription mDataFormat; // 2
AudioQueue mQueue; // 3
//AudioQueueBufferRef mBuffers[kNumberBuffers]; // 4
AudioFile mAudioFile; // 5
int bufferByteSize; // 6
int mCurrentPacket; // 7
boolean mIsRunning; // 8
public void startRecord(){
mQueue.start(null);
}
public void stopRecord(){
mQueue.stop(true);
}
#Callback
static void HandleInputBuffer(
AQRecorder aqData,
AudioQueue inAQ,
AudioQueueBuffer inBuffer,
AudioTimeStamp inStartTime,
int inNumPackets,
AudioStreamPacketDescription inPacketDesc
) {
AQRecorder pAqData = aqData; // 1
if (inNumPackets == 0 && pAqData.mDataFormat.mBytesPerPacket() != 0)
inNumPackets = inBuffer.mAudioDataByteSize() / pAqData.mDataFormat.mBytesPerPacket();
if (!aqData.mIsRunning) // 5
return;
System.out.println(inBuffer.mAudioData());
}
void setUpAudioFormat() throws NoSuchMethodException{
mDataFormat = new AudioStreamBasicDescription(
16000, // mSampleRate
AudioFormat.LinearPCM, // mFormatID
(1 << 2), // mFormatFlags
512, // mBytesPerPacket
1, // mFramesPerPacket
512, // mBytesPerFrame
1, // mChanneslPerFrame
16, // mBitsPerChannel
0 // mReserved
);
AudioQueuePtr mQueuePtr = new AudioQueuePtr();
mQueuePtr.set(mQueue);
VoidPtr self = new VoidPtr();
#SuppressWarnings("rawtypes")
Class[] cArg = new Class[6];
cArg[0] = AQRecorder.class;
cArg[1] = AudioQueue.class;
cArg[2] = AudioQueueBuffer.class;
cArg[3] = AudioTimeStamp.class;
cArg[4] = int.class;
cArg[5] = AudioStreamPacketDescription.class;
FunctionPtr handleInputBuffer = new FunctionPtr((AQRecorder.class).getDeclaredMethod("HandleInputBuffer", cArg));
AudioQueue.newInput(mDataFormat, handleInputBuffer, self, null, "", 0, mQueuePtr);
}
};
With RoboVM 1.0.0-beta-3 I was finally able to get audio record and playback working. Not sure why the recording audio queue takes up to 20 seconds to stop, but here is some sample code that works in the simulator and on my iPhone 4:
Main Class:
import java.util.Vector;
import org.robovm.apple.coregraphics.*;
import org.robovm.apple.foundation.*;
import org.robovm.apple.uikit.*;
public class TestAudioQueueCrash extends UIApplicationDelegateAdapter
{
private UIWindow window = null;
private int clickCount = 0;
#Override
public boolean didFinishLaunching(UIApplication application, UIApplicationLaunchOptions launchOptions)
{
final UIButton button = UIButton.create(UIButtonType.RoundedRect);
button.setFrame(new CGRect(15.0f, 121.0f, 291.0f, 37.0f));
button.setTitle("Click me!", UIControlState.Normal);
button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener()
{
#Override
public void onTouchUpInside(UIControl control, UIEvent event)
{
if (clickCount == 0)
{
button.setTitle("Recording for 5 seconds... (SPEAK!)", UIControlState.Normal);
Runnable r = new Runnable()
{
public void run()
{
try
{
clickCount = 1;
AudioRecord record = new AudioRecord();
record.startRecording();
long when = System.currentTimeMillis() + 5000;
final Vector<byte[]> v = new Vector();
byte[] ba = new byte[3072];
while (System.currentTimeMillis() < when)
{
int n = 0;
while (n<3072)
{
int i = record.read(ba, n, 3072-n);
if (i==-1 || i == 0) break;
n += i;
}
if (n>0)
{
byte[] ba2 = new byte[n];
System.arraycopy(ba, 0, ba2, 0, n);
v.addElement(ba2);
}
}
System.out.println("DONE RECORDING");
record.release();
System.out.println("RECORDER STOPPED");
System.out.println("Playing back recorded audio...");
button.setTitle("Playing back recorded audio...", UIControlState.Normal);
AudioTrack at = new AudioTrack();
at.play();
while (v.size() > 0)
{
ba = v.remove(0);
at.write(ba, 0, ba.length);
Thread.yield();
}
at.stop();
button.setTitle("DONE", UIControlState.Normal);
System.out.println("FINISHED PIPING AUDIO");
}
catch (Exception x)
{
x.printStackTrace();
button.setTitle("ERROR: " + x.getMessage(), UIControlState.Normal);
}
clickCount = 0;
}
};
new Thread(r).start();
}
}
});
window = new UIWindow(UIScreen.getMainScreen().getBounds());
window.setBackgroundColor(UIColor.lightGray());
window.addSubview(button);
window.makeKeyAndVisible();
return true;
}
public static void main(String[] args)
{
try (NSAutoreleasePool pool = new NSAutoreleasePool())
{
UIApplication.main(args, null, TestAudioQueueCrash.class);
}
}
}
AQRecorderState:
/*<imports>*/
import java.util.Hashtable;
import org.robovm.rt.bro.*;
import org.robovm.rt.bro.annotation.*;
import org.robovm.rt.bro.ptr.*;
/*</imports>*/
/*<javadoc>*/
/*</javadoc>*/
/*<annotations>*//*</annotations>*/
/*<visibility>*/public/*</visibility>*/ class /*<name>*/AQRecorderState/*</name>*/
extends /*<extends>*/Struct<AQRecorderState>/*</extends>*/
/*<implements>*//*</implements>*/ {
protected static Hashtable<Integer, AudioRecord> mAudioRecords = new Hashtable<>();
protected static int mLastID = 0;
/*<ptr>*/public static class AQRecorderStatePtr extends Ptr<AQRecorderState, AQRecorderStatePtr> {}/*</ptr>*/
/*<bind>*/
/*</bind>*/
/*<constants>*//*</constants>*/
/*<constructors>*/
public AQRecorderState() {}
public AQRecorderState(AudioRecord ar)
{
this.mID(++mLastID);
mAudioRecords.put(mID(), ar);
}
/*</constructors>*/
/*<properties>*//*</properties>*/
/*<members>*/
#StructMember(0) public native int mID();
#StructMember(0) public native AQRecorderState mID(int mID);
/*</members>*/
/*<methods>*//*</methods>*/
public AudioRecord getRecord()
{
return mAudioRecords.get(mID());
}
public static void drop(int mStateID)
{
mAudioRecords.remove(mStateID);
}
}
AudioRecord:
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.lang.reflect.Method;
import org.robovm.apple.audiotoolbox.AudioQueue;
import org.robovm.apple.audiotoolbox.AudioQueue.AudioQueuePtr;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer.AudioQueueBufferPtr;
import org.robovm.apple.audiotoolbox.AudioQueueError;
import org.robovm.apple.coreaudio.AudioFormat;
import org.robovm.apple.coreaudio.AudioStreamBasicDescription;
import org.robovm.apple.coreaudio.AudioStreamPacketDescription.AudioStreamPacketDescriptionPtr;
import org.robovm.apple.coreaudio.AudioTimeStamp.AudioTimeStampPtr;
import org.robovm.apple.coreaudio.CoreAudio;
import org.robovm.apple.corefoundation.CFRunLoopMode;
import org.robovm.rt.VM;
import org.robovm.rt.bro.Bro;
import org.robovm.rt.bro.Struct;
import org.robovm.rt.bro.annotation.Callback;
import org.robovm.rt.bro.annotation.Library;
import org.robovm.rt.bro.annotation.Pointer;
import org.robovm.rt.bro.ptr.FunctionPtr;
import org.robovm.rt.bro.ptr.VoidPtr;
/*<annotations>*/#Library("AudioToolbox")/*</annotations>*/
public class AudioRecord
{
protected double mSampleRate;
protected AudioFormat mFormatID;
protected int mFormatFlags;
protected int mBytesPerPacket;
protected int mFramesPerPacket;
protected int mBytesPerFrame;
protected int mChannelsPerFrame;
protected int mBitsPerChannel;
protected AudioQueue mQueue = null;
private int kNumberBuffers = 3;
private PipedInputStream mPIS;
private PipedOutputStream mPOS;
private int mStateID = -1;
private boolean mRunning = false;
public AudioRecord() throws IOException
{
mSampleRate = 44100;
mFormatID = AudioFormat.LinearPCM;
mFormatFlags = CoreAudio.AudioFormatFlagIsPacked | CoreAudio.AudioFormatFlagIsSignedInteger;
mBytesPerPacket = 2;
mFramesPerPacket = 1;
mBytesPerFrame = 2;
mChannelsPerFrame = 1;
mBitsPerChannel = 16;
mPOS = new PipedOutputStream();
mPIS = new PipedInputStream(mPOS);
}
public static int getMinBufferSize(int sampleRate, int channelConfig, int audioFormat)
{
// TODO Auto-generated method stub
return 0;
}
public int deriveBufferSize(AudioQueue audioQueue, AudioStreamBasicDescription ASBDescription, double seconds)
{
int maxBufferSize = 0x50000;
int maxPacketSize = ASBDescription.getMBytesPerPacket();
System.out.println(3);
double numBytesForTime = ASBDescription.getMSampleRate() * maxPacketSize * seconds;
return (int)(numBytesForTime < maxBufferSize ? numBytesForTime : maxBufferSize);
}
public void release()
{
System.out.println("RECORD QUEUE STOPPING...");
mRunning = false;
mQueue.stop(true);
// mQueue.dispose(true);
System.out.println("RECORD QUEUE STOPPED");
try
{
mPOS.close();
mPIS.close();
AQRecorderState.drop(mStateID);
}
catch (Exception x) { x.printStackTrace(); }
}
public int read(byte[] abData, int i, int length) throws IOException
{
return mPIS.read(abData, i, length);
}
/*<bind>*/static { Bro.bind(AudioRecord.class); }/*</bind>*/
/*<constants>*//*</constants>*/
/*<constructors>*//*</constructors>*/
/*<properties>*//*</properties>*/
/*<members>*//*</members>*/
#Callback
public static void callbackMethod(
#Pointer long refcon,
AudioQueue inAQ,
AudioQueueBuffer inBuffer,
AudioTimeStampPtr inStartTime,
int inNumPackets,
AudioStreamPacketDescriptionPtr inPacketDesc
)
{
try
{
System.out.println("a");
AQRecorderState.AQRecorderStatePtr ptr = new AQRecorderState.AQRecorderStatePtr();
ptr.set(refcon);
System.out.println("b");
AQRecorderState aqrs = ptr.get();
System.out.println("c");
byte[] ba = VM.newByteArray(inBuffer.getMAudioData().getHandle(), inBuffer.getMAudioDataByteSize());
System.out.println("d");
aqrs.getRecord().receive(ba);
System.out.println("e");
}
catch (Exception x) { x.printStackTrace(); }
inAQ.enqueueBuffer(inBuffer, 0, null);
System.out.println("f");
}
private void receive(byte[] ba)
{
if (mRunning) try { mPOS.write(ba); } catch (Exception x) { x.printStackTrace(); }
}
public void startRecording() throws Exception
{
AudioStreamBasicDescription asbd = new AudioStreamBasicDescription(mSampleRate, mFormatID, mFormatFlags, mBytesPerPacket, mFramesPerPacket, mBytesPerFrame, mChannelsPerFrame, mBitsPerChannel, 0);
AudioQueuePtr mQueuePtr = new AudioQueuePtr();
AudioQueueBufferPtr mBuffers = Struct.allocate(AudioQueueBufferPtr.class, kNumberBuffers);
System.out.println(11);
AQRecorderState aqData = new AQRecorderState(this);
mStateID = aqData.mID();
System.out.println(12);
Method callbackMethod = null;
Method[] methods = this.getClass().getMethods();
int i = methods.length;
while (i-->0) if (methods[i].getName().equals("callbackMethod"))
{
callbackMethod = methods[i];
break;
}
FunctionPtr fp = new FunctionPtr(callbackMethod );
System.out.println(13);
VoidPtr vp = aqData.as(VoidPtr.class);
System.out.println(14);
AudioQueueError aqe = AudioQueue.newInput(asbd, fp, vp, null, null, 0, mQueuePtr);
System.out.println(CFRunLoopMode.Common.value());
System.out.println(aqe.name());
mQueue = mQueuePtr.get();
System.out.println(2);
int bufferByteSize = deriveBufferSize(mQueue, asbd, 0.5);
System.out.println("BUFFER SIZE: "+bufferByteSize);
AudioQueueBufferPtr[] buffers = mBuffers.toArray(kNumberBuffers);
for (i = 0; i < kNumberBuffers; ++i)
{
mQueue.allocateBuffer(bufferByteSize, buffers[i]);
mQueue.enqueueBuffer(buffers[i].get(), 0, null);
}
mRunning = true;
mQueue.start(null);
}
}
AQPlayerState:
/*<imports>*/
import java.util.Hashtable;
import org.robovm.rt.bro.*;
import org.robovm.rt.bro.annotation.*;
import org.robovm.rt.bro.ptr.*;
/*</imports>*/
/*<javadoc>*/
/*</javadoc>*/
/*<annotations>*//*</annotations>*/
/*<visibility>*/public/*</visibility>*/ class /*<name>*/AQPlayerState/*</name>*/
extends /*<extends>*/Struct<AQPlayerState>/*</extends>*/
/*<implements>*//*</implements>*/ {
protected static Hashtable<Integer, AudioTrack> mAudioTracks = new Hashtable<>();
protected static int mLastID = 0;
/*<ptr>*/public static class AQPlayerStatePtr extends Ptr<AQPlayerState, AQPlayerStatePtr> {}/*</ptr>*/
/*<bind>*/
/*</bind>*/
/*<constants>*//*</constants>*/
/*<constructors>*/
public AQPlayerState() {}
public AQPlayerState(AudioTrack ar)
{
this.mID(++mLastID);
this.mID2(mLastID);
mAudioTracks.put(mID(), ar);
}
/*</constructors>*/
/*<properties>*//*</properties>*/
/*<members>*/
#StructMember(0) public native int mID();
#StructMember(0) public native AQPlayerState mID(int mID);
#StructMember(1) public native int mID2();
#StructMember(1) public native AQPlayerState mID2(int mID2);
/*</members>*/
/*<methods>*//*</methods>*/
public AudioTrack getTrack()
{
return mAudioTracks.get(mID());
}
public static void drop(int mStateID)
{
mAudioTracks.remove(mStateID);
}
}
AudioTrack:
import java.lang.reflect.Method;
import java.util.Vector;
import org.robovm.apple.audiotoolbox.AudioQueue;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer;
import org.robovm.apple.audiotoolbox.AudioQueue.AudioQueuePtr;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer.AudioQueueBufferPtr;
import org.robovm.apple.audiotoolbox.AudioQueueError;
import org.robovm.apple.audiotoolbox.AudioQueueParam;
import org.robovm.apple.coreaudio.AudioFormat;
import org.robovm.apple.coreaudio.AudioStreamBasicDescription;
import org.robovm.apple.coreaudio.CoreAudio;
import org.robovm.rt.bro.Bro;
import org.robovm.rt.bro.Struct;
import org.robovm.rt.bro.annotation.Callback;
import org.robovm.rt.bro.annotation.Pointer;
import org.robovm.rt.bro.ptr.BytePtr;
import org.robovm.rt.bro.ptr.FunctionPtr;
import org.robovm.rt.bro.ptr.VoidPtr;
public class AudioTrack {
public static final int MODE_STREAM = -1;
private int kNumberBuffers = 3;
private Vector<byte[]> mData = new Vector<>();
private int mStateID = -1;
private boolean mRunning = false;
protected double mSampleRate;
protected AudioFormat mFormatID;
protected int mFormatFlags;
protected int mBytesPerPacket;
protected int mFramesPerPacket;
protected int mBytesPerFrame;
protected int mChannelsPerFrame;
protected int mBitsPerChannel;
protected AudioQueue mQueue = null;
public AudioTrack()
{
mSampleRate = 44100;
mFormatID = AudioFormat.LinearPCM;
mFormatFlags = CoreAudio.AudioFormatFlagIsPacked | CoreAudio.AudioFormatFlagIsSignedInteger;
mBytesPerPacket = 2;
mFramesPerPacket = 1;
mBytesPerFrame = 2;
mChannelsPerFrame = 1;
mBitsPerChannel = 16;
}
public static int getMinBufferSize(int sampleRate, int channelConfigurationMono, int encodingPcm16bit)
{
// TODO Auto-generated method stub
return 0;
}
public int deriveBufferSize(AudioStreamBasicDescription ASBDescription, int maxPacketSize, double seconds)
{
int maxBufferSize = 0x50000;
int minBufferSize = 0x4000;
double numPacketsForTime = ASBDescription.getMSampleRate() / ASBDescription.getMFramesPerPacket() * seconds;
int outBufferSize = (int)(numPacketsForTime * maxPacketSize);
if (outBufferSize > maxBufferSize) return maxBufferSize;
if (outBufferSize < minBufferSize) return minBufferSize;
return outBufferSize;
}
/*<bind>*/static { Bro.bind(AudioTrack.class); }/*</bind>*/
/*<constants>*//*</constants>*/
/*<constructors>*//*</constructors>*/
/*<properties>*//*</properties>*/
/*<members>*//*</members>*/
#Callback
public static void callbackMethod(
#Pointer long refcon,
AudioQueue inAQ,
AudioQueueBuffer inBuffer
)
{
System.out.println("In Callback");
AQPlayerState.AQPlayerStatePtr ptr = new AQPlayerState.AQPlayerStatePtr();
ptr.set(refcon);
AQPlayerState aqps = ptr.get();
AudioTrack me = aqps.getTrack();
me.nextChunk(inAQ, inBuffer);
}
private void nextChunk(AudioQueue inAQ, AudioQueueBuffer inBuffer)
{
byte[] ba = null;
long when = System.currentTimeMillis() + 30000;
while (mRunning && System.currentTimeMillis() < when)
{
if (mData.size() > 0)
{
ba = mData.remove(0);
break;
}
try { Thread.yield(); } catch (Exception x) { x.printStackTrace(); }
}
if (ba == null) ba = new byte[0];
System.out.println("PLAYING BYTES: "+ba.length);
if (ba.length>0)
{
VoidPtr vp = inBuffer.getMAudioData();
BytePtr bp = vp.as(BytePtr.class); //Struct.allocate(BytePtr.class, ba.length);
bp.set(ba);
// inBuffer.setMAudioData(vp);
inBuffer.setMAudioDataByteSize(ba.length);
}
mQueue.enqueueBuffer(inBuffer, 0, null);
}
public void play()
{
final AudioTrack me = this;
Runnable r = new Runnable()
{
public void run()
{
AudioStreamBasicDescription asbd = new AudioStreamBasicDescription(mSampleRate, mFormatID, mFormatFlags, mBytesPerPacket, mFramesPerPacket, mBytesPerFrame, mChannelsPerFrame, mBitsPerChannel, 0);
AudioQueuePtr mQueuePtr = new AudioQueuePtr();
Method callbackMethod = null;
Method[] methods = me.getClass().getMethods();
int i = methods.length;
while (i-->0) if (methods[i].getName().equals("callbackMethod"))
{
callbackMethod = methods[i];
break;
}
FunctionPtr fp = new FunctionPtr(callbackMethod );
AQPlayerState aqData = new AQPlayerState(me);
mStateID = aqData.mID();
VoidPtr vp = aqData.as(VoidPtr.class);
// AudioQueueError aqe = AudioQueue.newOutput(asbd, fp, vp, CFRunLoop.getCurrent(), new CFString(CFRunLoopMode.Common.value()), 0, mQueuePtr);
AudioQueueError aqe = AudioQueue.newOutput(asbd, fp, vp, null, null, 0, mQueuePtr);
System.out.println(aqe.name());
mQueue = mQueuePtr.get();
int bufferByteSize = deriveBufferSize(asbd, 2, 0.5);
System.out.println("BUFFER SIZE: "+bufferByteSize);
System.out.println("Volume PARAM:"+(int)AudioQueueParam.Volume.value());
mQueue.setParameter((int)AudioQueueParam.Volume.value(), 1.0f);
mRunning = true;
AudioQueueBufferPtr mBuffers = Struct.allocate(AudioQueueBufferPtr.class, kNumberBuffers);
AudioQueueBufferPtr[] buffers = mBuffers.toArray(kNumberBuffers);
for (i = 0; i < kNumberBuffers; ++i)
{
mQueue.allocateBuffer(bufferByteSize, buffers[i]);
nextChunk(mQueue, buffers[i].get());
}
System.out.println("STARTING QUEUE");
mQueue.start(null);
System.out.println("QUEUE STARTED");
/*
System.out.println("RUNNING LOOP");
do
{
System.out.print(".");
CFRunLoop.runInMode(CFRunLoopMode.Default, 0.25, false);
System.out.print("#");
}
while (mRunning);
System.out.println("!!!");
CFRunLoop.runInMode(CFRunLoopMode.Default, 1, false);
System.out.println("DONE RUNNING LOOP");
mQueue.stop(true);
AQPlayerState.drop(mStateID);
System.out.println("QUEUE STOPPED");
*/
}
};
new Thread(r).start();
}
public void write(byte[] ba, int i, int length)
{
while (mData.size() > 10) Thread.yield();
System.out.println("SOUND IN: "+length+" bytes");
mData.addElement(ba);
}
public void stop()
{
System.out.println("STOPPING AUDIO PLAYER");
mRunning = false;
mQueue.stop(true);
AQPlayerState.drop(mStateID);
}
public void release()
{
// TODO Auto-generated method stub
}
}
I am trying to write a basic Java Swing application that works as such: an Arduino writes two CSV strings to the COM port every second, I read that data and parse it, and then display the data in a GUI. With some help from the official Arduino tutorials, I am at the point where I can read and parse the data coming in from the serial port, and display it. However, I would be able to pause and resume the GUI (while still reading from serial) using a key on the keyboard. I was able to pause the program, but cannot figure out how to resume it. I've been looking around online for a while now to no avail. If someone could point me in the right direction, I'd sincerely appreciate it. I'm rather new to threads and the Arduino, but have some basic Java experience. Here is the code below, which is broken up into three classes (one to read from serial, one for the GUI, and a main class to run everything. Thanks!
Here is the "Display" class:
//Imports
import java.awt.*;
import javax.swing.*;
import java.util.Random;
import java.lang.Thread;
import java.awt.event.*;
import java.util.ArrayList;
import java.awt.Toolkit.*;
//Class Definition
public class Display extends JPanel implements Runnable {
//Variables
private volatile Thread t;
private volatile boolean threadSuspended;
//Data Strings
private String data;
private String carData;
private String gpsData;
//Car Data
private int event;
private int engineRPM;
private int cvtRPM;
private double kph;
private int brake;
private double accel_x;
private double accel_y;
private double accel_z;
private double gyro_x;
private double gyro_y;
private double gyro_z;
//GPS Data
private String sentence;
private int time;
private String status;
private double latitude;
private String latitudeDir;
private double longitude;
private String longitudeDir;
private double knots;
private double angle;
private int date;
private double magVar;
private char magVarDir;
private String checksum;
//Constructor
public Display() {
super.setSize(800,800);
setLayout(new BorderLayout());
addKeyListener(new KeyHandler());
setFocusable(true);
start();
}
public void start() {
t = new Thread(this);
t.start();
threadSuspended = false;
}
public synchronized void stop() {
t = null;
notify();
threadSuspended = true;
}
#Override
public void run() {
Thread thisThread = Thread.currentThread();
while(t == thisThread) {
try {
Thread.sleep(1000);
synchronized(this) {
while (threadSuspended && t == thisThread) {
wait();
}
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
repaint();
save();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
kph = SerialReader.getKPH();
System.out.println(kph);
g.drawString("Speed of Car: " + kph, 35, 35);
}
//Writes values to a file and updates average values
public void save() {
//Nothing here yet
}
private class KeyHandler extends KeyAdapter {
#Override
public synchronized void keyPressed(KeyEvent ke) {
ke.consume();
int key = ke.getKeyCode();
if (key == KeyEvent.VK_SPACE) {
threadSuspended = !threadSuspended;
if (!threadSuspended) {
notify();
}
}
}
}
}
Here is the "SerialReader" class:
//Imports
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.util.Enumeration;
//Class definition
public class SerialReader implements SerialPortEventListener {
//Data Strings
private static String data;
private static String carData;
private static String gpsData;
//Car Data
private static int event;
private static int engineRPM;
private static int cvtRPM;
private static double kph;
private static int brake;
private static double accel_x;
private static double accel_y;
private static double accel_z;
private static double gyro_x;
private static double gyro_y;
private static double gyro_z;
//GPS Data
private static String sentence;
private static int time;
private static String status;
private static double latitude;
private static String latitudeDir;
private static double longitude;
private static String longitudeDir;
private static double knots;
private static double angle;
private static int date;
private static double magVar;
private static char magVarDir;
private static String checksum;
//Variables
SerialPort serialPort;
private static final String[] PORT_NAMES = {"/dev/tty.usbserial-A9007UX1", "/dev/ttyUSB0", "COM3"};
private BufferedReader input;
private OutputStream output;
private static final int TIME_OUT = 2000;
private static final int DATA_RATE = 9600;
//Constructor
public SerialReader() {}
//Opens port
public void initialize() {
CommPortIdentifier portId = null;
Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();
while (portEnum.hasMoreElements()) {
CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
for (String portName : PORT_NAMES) {
if (currPortId.getName().equals(portName)) {
portId = currPortId;
break;
}
}
}
if (portId == null) {
System.out.println("Could not find COM port.");
return;
}
try {
// open serial port, and use class name for the appName.
serialPort = (SerialPort) portId.open(this.getClass().getName(),
TIME_OUT);
// set port parameters
serialPort.setSerialPortParams(DATA_RATE,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
// open the streams
input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
output = serialPort.getOutputStream();
// add event listeners
serialPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true);
} catch (Exception e) {
//Do nothing
}
}
//Called when port is no longer in use
public synchronized void close() {
if (serialPort != null) {
serialPort.removeEventListener();
serialPort.close();
System.out.println("Port Closed");
}
}
//Reads data from serial port
public synchronized void serialEvent(SerialPortEvent spe) {
if (spe.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
data = input.readLine();
//System.out.println(data);
if (data.charAt(0) == '$') {
gpsData = data;
parseGPSData(data);
} else {
carData = data;
parseCarData(data);
}
} catch (Exception e) {
//Do nothing
}
}
}
//Parse car data
public void parseCarData(String data) {
String[] info = data.split(",");
event = Integer.parseInt(info[0]);
engineRPM = Integer.parseInt(info[1]);
cvtRPM = Integer.parseInt(info[2]);
kph = Double.parseDouble(info[3]);
brake = Integer.parseInt(info[4]);
accel_x = Double.parseDouble(info[5]);
accel_y = Double.parseDouble(info[6]);
accel_z = Double.parseDouble(info[7]);
gyro_x = Double.parseDouble(info[8]);
gyro_y = Double.parseDouble(info[9]);
gyro_z = Double.parseDouble(info[10]);
}
//Parse GPS data
public void parseGPSData(String data) {
String[] info = data.split(",");
sentence = info[0];
int time = Integer.parseInt(info[1]);
status = info[2];
latitude = Double.parseDouble(info[3]);
latitudeDir = info[4];
longitude = Double.parseDouble(info[5]);
longitudeDir = info[6];
knots = Double.parseDouble(info[7]);
angle = Double.parseDouble(info[8]);
date = Integer.parseInt(info[9]);
magVar = Double.parseDouble(info[10]);
magVarDir = info[11].charAt(0);
checksum = info[11].substring(1);
}
//Accessor Methods
public static String getCarData() {
return carData;
}
public static String getGPSData() {
return gpsData;
}
public static int getEvent() {
return event;
}
public static int getEngineRPM() {
return engineRPM;
}
public static int getCvtRPM() {
return cvtRPM;
}
public static double getKPH() {
return kph;
}
public static int getBrake() {
return brake;
}
public static double getAccel_X() {
return accel_x;
}
public static double getAccel_Y() {
return accel_y;
}
public static double getAccel_Z() {
return accel_z;
}
public static double getGyro_X() {
return gyro_x;
}
public static double getGyro_Y() {
return gyro_y;
}
public static double getGyro_Z() {
return gyro_z;
}
public static String getSentence() {
return sentence;
}
public static int getTime() {
return time;
}
public static String getStatus() {
return status;
}
public static double getLatitude() {
return latitude;
}
public static String getLatitudeDir() {
return latitudeDir;
}
public static double getLongitude() {
return longitude;
}
public static String getLongitudeDir() {
return longitudeDir;
}
public static double getKnots() {
return knots;
}
public static double getAngle() {
return angle;
}
public static int getDate() {
return date;
}
public static double getMagVar() {
return magVar;
}
public static char getMagVarDir() {
return magVarDir;
}
public static String getChecksum() {
return checksum;
}
}
And lastly, the "Main" class:
//Imports
import java.awt.*;
import javax.swing.*;
//Class Definition
public class Main {
//Variables
private static JFrame frame;
private static Display display;
private static Container pane;
private static Dimension dim;
private static SerialReader sr;
private static Thread t;
//Run the program
public static void main(String[] args) {
sr = new SerialReader();
sr.initialize();
t = new Thread() {
public void run() {
try {
Thread.sleep(1000000);
} catch (InterruptedException ie) {
//Do nothing
}
}
};
t.start();
System.out.println("Started");
//Set look and feel to that of OS
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
frame = new JFrame("Baja GUI, Version 0.1");
frame.setSize(800,800);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
display = new Display();
pane = frame.getContentPane();
pane.add(display);
//Place frame in the middle of the screen
dim = Toolkit.getDefaultToolkit().getScreenSize();
frame.setLocation(dim.width/2-frame.getSize().width/2, dim.height/2-frame.getSize().height/2);
frame.setVisible(true);
}
}