Java midi volume control won't work - java

I've been trying to get midi volume control to work in a MidiPlayer class for a very long time now. I've searched for examples for accomplishing this here on stackoverflow and all over the Internet, but nothing I try ever seems to work. The volume stays the same! It doesn't change like I want it to.
I'm running Java 1.6.0_32 on Windows 7 professional.
Here! Have an SSCCE:
import java.io.*;
import javax.sound.midi.*;
import java.net.URL;
public class MidiSSCCE {
public static void main(String[] args)
{
// creates the midi player and sets up its sequencer and synthesizer.
MidiPlayer midiP = new MidiPlayer();
double volume = 1.0;
// loads a midi from a url into the sequencer, but doesn't start playing it yet.
midiP.load("http://www.vgmusic.com/music/computer/microsoft/windows/touhou_6_stage3_boss.mid",true);
// set the midi to loop indefinitely.
midiP.loop(-1);
// start playing the midi.
midiP.play(true);
// while loop changes the volume of the midi while it is playing.
while(true) {
midiP.setVolume(volume);
try { Thread.sleep(300); } catch(Exception e) {}
volume -= 0.1;
if(volume < 0) volume += 1.0;
}
}
}
/**
* MidiPlayer
* author: Stephen Lindberg
* Last modified: Oct 14, 2011
*
* A class that allows midi files to be loaded and played.
**/
class MidiPlayer {
private Sequence seq;
private Sequencer seqr;
private Synthesizer synth;
private Receiver receiver;
private File midiFile;
private String midiID;
private boolean loaded;
private boolean usingHardwareSoundbank;
// CONSTRUCTORS
public MidiPlayer() {
loaded = false;
try {
// obtain the sequencer and synthesizer.
seqr = MidiSystem.getSequencer();
synth = MidiSystem.getSynthesizer();
// print the user's midi device info
System.out.println("Setting up Midi Player...");
System.out.println("MidiDeviceInfo: ");
for(MidiDevice.Info info : MidiSystem.getMidiDeviceInfo()) {
System.out.println("\t" + info.getName() + ": " +info.getDescription());
}
System.out.println();
// obtain the soundbank and receiver.
Soundbank soundbank = synth.getDefaultSoundbank();
if(soundbank == null) {
receiver = MidiSystem.getReceiver();
usingHardwareSoundbank = true;
System.out.println("using hardware soundbank");
}
else {
synth.loadAllInstruments(soundbank);
receiver = synth.getReceiver();
usingHardwareSoundbank = false;
System.out.println("using default software soundbank:" + soundbank);
}
seqr.getTransmitter().setReceiver(receiver);
}
catch(Exception e) {
System.out.println("MIDI error: I just don't know what went wrong! 6_9");
}
}
// DATA METHODS
/**
* load(String fileName)
* loads a midi file into this MidiPlayer.
* Preconditions: fileName is the name of the midi file to be loaded.
* Postconditions: fileName is loaded and is ready to be played.
**/
public void load(String fileName, boolean isOnline) {
this.unload();
try {
URL midiURL;
if(isOnline) midiURL = new URL(fileName);
else midiURL = getClass().getClassLoader().getResource(fileName);
seq = MidiSystem.getSequence(midiURL);
seqr.open();
synth.open();
// load our sequence into the sequencer.
seqr.setSequence(seq);
loaded = true;
}
catch(IOException ioe) {
System.out.println("MIDI error: Problem occured while reading " + midiFile.getName() + ".");
}
catch(InvalidMidiDataException imde) {
System.out.println("MIDI error: " + midiFile.getName() + " is not a valid MIDI file or is unreadable.");
}
catch(Exception e) {
System.out.println("MIDI error: I just don't know what went wrong! 6_9");
}
}
/**
* unload()
* Unloads the current midi from the MidiPlayer and releases its resources from memory.
**/
public void unload() {
this.stop();
seqr.close();
synth.close();
midiFile = null;
loaded = false;
}
// OTHER METHODS
/**
* play(boolean reset)
* plays the currently loaded midi.
* Preconditions: reset tells our midi whether or nor to begin playing from the start of the midi file's current loop start point.
* Postconditions: If reset is true, then the loaded midi begins playing from its loop start point (default 0).
* If reset is false, then the loaded midi resumes playing from its current position.
**/
public void play(boolean reset) {
if(reset) seqr.setTickPosition(seqr.getLoopStartPoint());
seqr.start();
}
/**
* stop()
* Pauses the current midi if it was playing.
**/
public void stop() {
if(seqr.isOpen()) seqr.stop();
}
/**
* isRunning()
* Returns true if the current midi is playing. Returns false otherwise.
**/
public boolean isRunning() {
return seqr.isRunning();
}
/**
* loop(int times)
* Sets the current midi to loop from start to finish a specific number of times.
* Preconditions: times is the number of times we want our midi to loop.
* Postconditions: The current midi is set to loop times times.
* If times = -1, the current midi will be set to loop infinitely.
**/
public void loop(int times)
{
loop(times,0,-1);
}
/**
* loop(int times)
* Sets the current midi to loop from a specified start point to a specified end point a specific number of times.
* Preconditions: times is the number of times we want our midi to loop.
* start is our loop's start point in ticks.
* end is our loop's end point in ticks.
* Postconditions: The current midi is set to loop from tick start to tick end times times.
* If times = -1, the current midi will be set to loop infinitely.
**/
public void loop(int times, long start, long end) {
if(start < 0) start = 0;
if(end > seqr.getSequence().getTickLength() || end <= 0) end = seqr.getSequence().getTickLength();
if(start >= end && end != -1) start = end-1;
seqr.setLoopStartPoint(start);
seqr.setLoopEndPoint(end);
if(times == -1) seqr.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
else seqr.setLoopCount(times);
}
public void setVolume(double vol) {
System.out.println("Midi volume change request: " + vol);
try {
if(usingHardwareSoundbank) {
ShortMessage volumeMessage = new ShortMessage();
for ( int i = 0; i < 16; i++ ) {
volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, i, 7, (int)(vol*127) );
receiver.send( volumeMessage, -1 );
}
}
else {
MidiChannel[] channels = synth.getChannels();
for( int c = 0; c < channels.length; c++ ) {
if(channels[c] != null) channels[c].controlChange( 7, (int)( vol*127) );
}
}
}
catch ( Exception e ) {
e.printStackTrace();
}
}
}
I've tried examples at the following sources with no success:
http://www.java2s.com/Code/Java/Development-Class/SettingtheVolumeofPlayingMidiAudio.htm
How to controll the MIDI channel's volume
https://forums.oracle.com/forums/thread.jspa?messageID=5389030
MIDI Song with CC
http://www.codezealot.org/archives/27
http://www.exampledepot.com/egs/javax.sound.midi/Volume.html

I'm struggling with these very issues on controlling sound and i've found a code to change volume that works. Unfurtunatly i was unable to understand it, but here is something in your code that is differente from the one i've seen. Maybe it can helps you.
Try change the line
seqr = MidiSystem.getSequencer();
for
seqr = MidiSystem.getSequencer(false);
Maybe it helps you, I believe that using the "false" the sequencer will connect to the receiver, and not to the synthesizer, then when you send the message to the receiver to set volume it will work.

public void setVolume(double vol) {
System.out.println("Midi volume change request: " + vol);
try {
if(usingHardwareSoundbank) {
ShortMessage volumeMessage = new ShortMessage();
for ( int i = 0; i < 16; i++ ) {
volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, i, 7, (int)(vol*127) );
receiver.send( volumeMessage, -1 );
}
}
else {
MidiChannel[] channels = synth.getChannels();
for( int c = 0; c < channels.length; c++ ) {
if(channels[c] != null) channels[c].controlChange( 7, (int)( vol*127) );
}
}
// Very important!
seqr.setSequence(seq);
}
catch ( Exception e ) {
e.printStackTrace();
}
}

Related

Arduino to Java data sent through USB

I am trying to get Accelerometer data into my Java application from Arduino at its IMU. When I run my Arduino program on Arduino IDE and bring up Serial Monitor data flow is really good. Unfortunatelly when I try to run my Java code something strange happens. I have to wait for 10 seconds and then about 1000 lines appear immediately. Then i have to wait for another 10-20 seconds and another 1000 lines of data appears.. Is Arduino sending data in packages ? Can't I get a single value right after its given to me from IMU ?
Arduino code :
#include <MPU9250.h>
#include <quaternionFilters.h>
#define AHRS true // Set to false for basic data read
#define SerialDebug true // Set to true to get Serial output for debugging
// Pin definitions
int intPin = 12; // These can be changed, 2 and 3 are the Arduinos ext int pins
int myLed = 13; // Set up pin 13 led for toggling
MPU9250 myIMU;
void setup()
{
Wire.begin();
Serial.begin(38400);
pinMode(intPin, INPUT);
digitalWrite(intPin, LOW);
pinMode(myLed, OUTPUT);
digitalWrite(myLed, HIGH);
byte c = myIMU.readByte(MPU9250_ADDRESS, WHO_AM_I_MPU9250);
if (c == 0x71) // WHO_AM_I should always be 0x68
{
myIMU.MPU9250SelfTest(myIMU.SelfTest);
myIMU.calibrateMPU9250(myIMU.gyroBias, myIMU.accelBias);
myIMU.initMPU9250();
} // if (c == 0x71)
else
{
while(1) ;
}
}
void loop()
{
if (myIMU.readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01)
{
myIMU.readAccelData(myIMU.accelCount); // Read the x/y/z adc values
myIMU.getAres();
myIMU.ax = (float)myIMU.accelCount[0]*myIMU.aRes; // - accelBias[0];
myIMU.ay = (float)myIMU.accelCount[1]*myIMU.aRes; // - accelBias[1];
myIMU.az = (float)myIMU.accelCount[2]*myIMU.aRes; // - accelBias[2];
}
myIMU.delt_t = millis() - myIMU.count;
if (myIMU.delt_t>20) // waiting some time to get less data
{
if(SerialDebug)
{
Serial.println(myIMU.ay*1000);
}
myIMU.count = millis();
myIMU.sumCount = 0;
myIMU.sum = 0;
}
}
Java code :
package perkusja;
import com.fazecast.jSerialComm.*;
import java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.JSlider;
public class Perkusja {
public static void main(String[] args) {
SerialPort[] ports = SerialPort.getCommPorts();
System.out.println("Select a port:");
int i = 1;
for(SerialPort port : ports)
System.out.println(i++ + ": " + port.getSystemPortName());
Scanner s = new Scanner(System.in);
int chosenPort = s.nextInt();
SerialPort serialPort = ports[chosenPort - 1];
if(serialPort.openPort())
System.out.println("Port opened successfully.");
else {
System.out.println("Unable to open the port.");
return;
}
serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING, 0, 0);
Scanner data = new Scanner(serialPort.getInputStream());
int counter =0;
while(data.hasNextLine()){
try{
// float kupa = data.nextFloat();
System.out.println(data.nextLine());
System.out.println("Line number : " + counter++);
}catch(Exception e){
System.out.println("ERROR");}
}
}
}
I've been following this tutorial : https://www.youtube.com/watch?v=8B6j_yr9H8g&t=392s
Im using Arduino ProMicro from sparkfun and IMU - MPU9250 also from sparkfun

private method call between classes

trying to get areasConnected to display. As you can see I have attemped (poorly I know) to substitute this method with the nextArea method. Will post the GameWorld class below this one. Please note that I DO NOT want someone to do the work for me, just point me in the right direction.
/**
* Auto Generated Java Class.
*/
import java.util.Scanner;
public class WereWolfenstein2D {
GameWorld gameOne = new GameWorld(true);
private boolean tracing = false;
Scanner sc = new Scanner(System.in);
String input, answer, restart;
int walk, current;
char area;
public void explain() {
System.out.print("You are a hunter, defend the village and capture the Werewolf by shooting it three times. ");
System.out.println("If you get bitten visit the village to be cured. " +
"If you get bitten twice you'll turn." +
"If none of the town folk survive, you lose. " +
"Now choose how what circumstances you're willing to work in.");
}
public void pickDifficulity(){
{
System.out.println("Easy, Normal or Hard?");
input = sc.nextLine();
input = input.toUpperCase();
if (input.equals("EASY")){
System.out.println(Difficulty.EASY);
}
else if (input.equals("NORMAL")) {
System.out.println(Difficulty.NORMAL);
}
else if (input.equals("HARD")) {
System.out.println(Difficulty.HARD);
}
}
}
public void preMove(){
System.out.println("current stats");
// no. of actions until dawn
gameOne.checkForDawn();
// current population
gameOne.getVillagePopulation();
// if they can hear village
gameOne.isVillageNear();
// if not bitten:
gameOne.getShotCount();
// current area no.
gameOne.getCurrentArea();
// number of connecting areas
// gameOne.nextArea(char direction);
//gameOne.areasConnected(int s1, int s2);
// no of times werewolf shot
// no. of bullets left
gameOne.getShotCount();
// if the player can smell the wolf
gameOne.werewolfNear();
}
public void pickMove(){
System.out.print("walk shoot, quit or reset?");
answer = sc.nextLine();
//walk
//if area not connected: alert
//if into village: alert that they are healed
//if containing wolf: altert they are bitten, if already bitten game ends
switch(answer) {
case "walk": System.out.println("Pick a direction to walk in south(s), east(e), north(n), west(w)");
current = gameOne.getCurrentArea(); //current area they are in
char direction = sc.next().charAt(current); //direction they wish to travel
// char area = sc.next().charAt(current);
char area1 = 's';
char area2 = 'e';
char area3 = 'n';
char area4 = 'w';
System.out.println("the area to the south is: " + gameOne.nextArea(area1));
System.out.println("the area to the east is: " + gameOne.nextArea(area2));
System.out.println("the area to the north is: " + gameOne.nextArea(area3));
System.out.println("the area to the west is: " + gameOne.nextArea(area4));
System.out.println(current +"<< >>" + direction);
System.out.println("pick again");
int newcurrent = direction;
direction = sc.next().charAt(newcurrent);
System.out.println(newcurrent + "<< new >>" + direction);
break;
case "shoot":
break;
case "quit": System.out.print("would you like to start again? (yes/no)");
restart = sc.nextLine();
if (restart.equals("yes")) {
gameOne.reset();
}
else {
System.out.println("Thanks for playing");
}
break;
case "reset": //gameOne.reset();
this.pickDifficulity();
break;
}
}
public void play() {
this.explain();
this.pickDifficulity();
this.preMove();
this.pickMove();
// gameOne.reset();
}
public void setTracing(boolean onOff) {
tracing = onOff;
}
public void trace(String message) {
if (tracing) {
System.out.println("WereWolfenstein2D: " + message);
}
}
}
GAMEWORLD CLASS>>>
public class GameWorld {
// Final instance variables
public final int NIGHT_LENGTH = 3; // three actions until dawn arrives (day is instantaneous)
private final int MAX_SHOTS_NEEDED = 3; // successful hits required to subdue werewolf
//This map is _deliberately_ confusing, although it actually a regular layout
private int[] east = {1,2,0,4,5,3,7,8,6}; // areas to east of current location (index)
private int[] west = {2,0,1,5,3,4,8,6,7}; // areas to west of current location (index)
private int[] north = {6,7,8,0,1,2,3,4,5}; // areas to north of current location (index)
private int[] south = {3,4,5,6,7,8,0,1,2}; // areas to south of current location (index)
private int numAreas = south.length; // number of areas in the "world"
// Non-final instance variables
private int currentArea; // current location of player
private int villagePos; // area where the village is located
private int wolfPos; // area where the werewolf can be found
private Difficulty level; // difficulty level of the game
private int villagerCount; // number of villagers remaining
private int stepsUntilDawn; // number of actions until night falls
private boolean isBitten; // is the player currently bitten and in need of treatment?
private int hitsRemaining; // number of shots still needed to subdue the werewolf
private Random generator; // to use for random placement in areas
private boolean tracing; // switch for tracing messages
/**
* Creates a game world for the WereWolfenstein 2D game.
* #param traceOnOff whether or not tracing output should be shown
*/
public GameWorld(boolean traceOnOff) {
trace("GameWorld() starts...");
generator = new Random();
generator.setSeed(101); //this default setting makes the game more predictable, for testing
setTracing(traceOnOff); //this may replace random number generator
trace("...GameWorld() ends");
}
/**
* Returns the number of the current area.
* #return which area number player is within
*/
public int getCurrentArea() {
trace("getCurrentArea() starts... ...and ends with value " + currentArea);
return currentArea;
}
/**
* Returns the number of shot attempts, formatted as "total hits/total required"
* #return the fraction of shots that have hit the werewolf (out of the required number of hits)
*/
public String getShotCount() {
String count; // formatted total
trace("getShotCount() starts...");
count = (MAX_SHOTS_NEEDED - hitsRemaining) + "/" + MAX_SHOTS_NEEDED;
trace("getShotCount() ...ends with value " + count);
return count;
}
/**
* Returns the current number of villagers.
* #return the villager count, >= 0
*/
public int getVillagePopulation() {
return villagerCount;
}
/**
* Returns the number of actions remaining until dawn arrives.
* #return actions remaining until dawn event
*/
public int getActionsUntilNight() {
return stepsUntilDawn;
}
/**
* Randomly determines a unique starting location (currentArea), village
* position (villagePos) and werewolf position (wolfPos).
* #param difficulty - the difficulty level of the game
* #return the starting location (area)
*/
public int newGame(Difficulty difficulty) {
trace("newGame() starts...");
level = difficulty;
//determine village location and initialise villagers and night length
villagePos = generator.nextInt(numAreas);
stepsUntilDawn = NIGHT_LENGTH;
villagerCount = level.getVillagerCount();
// determine player's position
if (level.getPlayerStartsInVillage()) {
//place player in village
currentArea = villagePos;
} else {
//pick a random location for player away from the village
do {
currentArea = generator.nextInt(numAreas);
} while (currentArea == villagePos);
}
trace("player starts at " + currentArea);
// determine werewolf's position
trace("calling resetTargetPosition()");
resetWolfPosition();
// define player's status
trace("player is not bitten");
isBitten = false;
trace("werewolf is not hit");
hitsRemaining = MAX_SHOTS_NEEDED;
trace("...newGame() ends with value " + currentArea);
return currentArea;
}
/** Randomly determines a unique location for werewolf (wolfPos). */
private void resetWolfPosition() {
int pos; // werewolf position
trace("resetWolfPosition() starts...");
pos = generator.nextInt(numAreas);
while (pos == currentArea || pos == villagePos) {
trace("clash detected");
// avoid clash with current location
pos = generator.nextInt(numAreas);
}
wolfPos = pos;
trace("werewolf position is " + wolfPos);
trace("...resetWolfPosition() ends");
}
/**
* Returns the nearness of the werewolf.
* #return Status of werewolf's location
* BITTEN: if player is currently bitten (and delirious)
* NEAR: if werewolf is in a connected area
* FAR: if werewolf is elsewhere
*/
public Result werewolfNear() {
trace("werewolfNear() starts");
if (isBitten) {
trace("werewolfNear(): player is still delirious from a bite so cannot sense nearness of werewolf");
return Result.BITTEN;
}
trace("werewolfNear() returning result of nearnessTo(" + wolfPos + ")");
return nearnessTo(wolfPos);
}
/**
* Returns true if the village is near the player (in an adjacent area),
* false otherwise.
* #return true if the player is near (but not in) the village, false otherwise.
*/
public boolean isVillageNear() {
trace("villageNear() starts and returns result of nearnessTo(" + villagePos + ") == Result.NEAR");
return nearnessTo(villagePos) == Result.NEAR;
}
/**
* Returns the nearness of the player to the nominated area.
* #param area the area (werewolf or village) to assess
* #return Nearness of player to nominated area
* NEAR: if player is adjacent to area
* FAR: if player is not adjacent to the area
*/
private Result nearnessTo(int area) {
Result closeness; // closeness of player to area
trace("nearnessTo() starts...");
if ((east[currentArea] == area) ||
(west[currentArea] == area) ||
(north[currentArea] == area) ||
(south[currentArea] == area))
{
// player is close to area
closeness = Result.NEAR;
trace("area is close");
} else {
// player is not adjacent to area
closeness = Result.FAR;
trace("area is far");
}
trace("...nearnessTo() ends with value " + closeness);
return closeness;
}
/**
* Try to move the player to another area. If the move is not IMPOSSIBLE
* then the number of actions remaining before dawn arrives is decremented.
* #param into the area to try to move into
* #return Result of the movement attempt
* SUCCESS: move was successful (current position changed)
* VILLAGE: move was successful and player has arrived in the village (and is not longer bitten)
* BITTEN: move was successful but player encountered the werewolf
* FAILURE: move was successful but already bitten player encountered the werewolf again
* IMPOSSIBLE: move was impossible (current position not changed)
*/
public Result tryWalk(int into) {
Result result; // outcome of walk attempt
trace("tryWalk() starts...");
if (areasConnected(currentArea, into)) {
trace("move into area " + into );
currentArea = into;
if (currentArea != wolfPos) {
// werewolf not found
trace("werewolf not encountered");
result = Result.SUCCESS;
if (currentArea == villagePos) {
isBitten = false;
result = Result.VILLAGE;
}
} else {
// werewolf encountered
if (isBitten) {
trace("werewolf encountered again");
result = Result.FAILURE;
} else {
// not bitten
trace("werewolf encountered");
result = Result.BITTEN;
isBitten = true;
resetWolfPosition();
}
}
stepsUntilDawn--; //one more action taken
} else { // area not connected
trace("move not possible");
result = Result.IMPOSSIBLE;
}
trace("...tryWalk() ends with value " + result);
return result;
}
/**
* Try to shoot a silver bullet at the werewolf from the current area.
* If the shot is not IMPOSSIBLE then the number of actions remaining
* before dawn arrives is decremented.
* #param into the area to attempt to shoot into
* #return status of attempt
* CAPTURED: werewolf has been subdued and captured
* SUCCESS: werewolf has been hit but is not yet captured
* VILLAGE: the shot went into the village and a villager has died
* FAILURE: werewolf not present
* IMPOSSIBLE: area not connected
*/
public Result shoot(int into) {
Result result; // outcome of shooting attempt
trace("shoot() starts...");
if (areasConnected(currentArea, into)) {
// area connected
trace("shoot into area " + into );
if (into == villagePos) {
result = Result.VILLAGE;
villagerCount--;
trace("shot into village");
} else if (into != wolfPos) {
// not at werewolf location (but at least didn't shoot into the village!)
result = Result.FAILURE;
trace("werewolf not present");
} else {
// at werewolf location
hitsRemaining--;
if (hitsRemaining == 0) {
// last hit required to subdue the werewolf
trace("werewolf subdued and captured");
result = Result.CAPTURED;
} else {
// not the last shot
result = Result.SUCCESS;
if (level.getWolfMovesWhenShot()) {
resetWolfPosition();
}
trace("werewolf found but not yet captured");
}
}
stepsUntilDawn--; //one more action taken
} else {
// not at valid location
result = Result.IMPOSSIBLE;
trace("area not present");
}
trace("...shoot() ends with value " + result);
return result;
}
/**
* Checks if there are no more actions left until dawn arrives. If dawn is
* here then decrements the number of villagers, repositions the werewolf
* and resets the number of actions until dawn arrives again. Returns true
* if dawn occurred, false if it did not.
* #return true if dawn just happened, false if has not yet arrived
*/
public boolean checkForDawn() {
if (stepsUntilDawn == 0) {
if (villagerCount > 0) { //dawn may arrive after shooting the last villager
villagerCount--;
}
stepsUntilDawn = NIGHT_LENGTH;
resetWolfPosition();
return true;
}
return false;
}
/**
* Returns true if areas s1 and s2 are connected, false otherwise.
* Also returns false if either area is an invalid area identifier.
* #param s1 the first area
* #param s2 the second area
* #return true if areas are connected, false otherwise
*/
private boolean areasConnected(int s1, int s2) {
if (Math.min(s1, s2) >= 0 && Math.max(s1, s2) < numAreas) { //valid areas...
//...but are they connected?
return east[s1] == s2 || north[s1] == s2 || west[s1] == s2 || south[s1] == s2;
}
//Either s1 or s2 is not a valid area identifier
return false;
}
/**
* Determine ID number of an adjacent area given its direction from the
* current area.
* #param direction the direction to look (n for north, e for east, s for south, w for west)
* #return number of the area in that direction
* #throws IllegalArgumentException if direction is null
*/
public int nextArea(char direction) {
int nextIs; // area number of area in indicated direction
//Valid values
final char N = 'n', E = 'e', S = 's', W = 'w';
trace("nextArea() starts...");
// examine adjacent areas
switch (direction) {
case N: trace("determining number of area to the north");
nextIs = north[currentArea];
break;
case E: trace("determining number of area to the east");
nextIs = east[currentArea];
break;
case S: trace("determining number of area to the south");
nextIs = south[currentArea];
break;
case W: trace("determining number of area to the west");
nextIs = west[currentArea];
break;
default: throw new IllegalArgumentException("Direction must be one of " + N + ", " + E + ", " + S + " or " + W);
}
trace("...nextArea() ends with value for '" + direction + "' of " + nextIs);
return nextIs;
}
/** Resets all game values. */
public void reset() {
trace("reset() starts...");
// reset all game values
trace("resetting all game values");
newGame(level); //start a new game with the same difficulty
trace("...reset() ends");
}
/**
* Turn tracing messages on or off. If off then it is assumed that
* debugging is not occurring and so a new (unseeded) random number
* generator is created so the game is unpredictable.
* #param shouldTrace indicates if tracing messages should be displayed or not
*/
public void setTracing(boolean shouldTrace) {
if (! shouldTrace) { // not tracing so get an unseeded RNG
generator = new Random();
}
tracing = shouldTrace;
}
/**
* Prints the given tracing message if tracing is enabled.
* #param message the message to be displayed
*/
public void trace(String message) {
if (tracing) {
System.out.println("GameWorld: " + message);
}
}
}
You can use reflection. Actually, it's the only way out if you want to directly access the private methods/fields of an object or a class.

How to make sure an instance is terminated?

I have easily written a Java method which waits for instance to change state from say "pending" to "running". It periodically (with delay of N seconds) polls the instance status using DescribeInstanceRequest.
However, it is tricky to find out whether it is really terminated as I never get the "terminated" instance status. Here is what I use at the moment, but I am not sure whether it is a good way...
/**
* Checks up to 20 times, with linearly increasing delay, for instance state change from the argFromState to the
* argToState.
*
* Typically you would call this method to wait until instance is "terminated", or when instance is "running".
*
* #param argInstanceID
* #param argFromState
* #param argToState
* #see waitForInstance(String argInstanceID)
*/
public void waitForStateChange(String argInstanceID, String argFromState, String argToState) {
List<InstanceStatus> statuses = null;
DescribeInstanceStatusRequest describeInstanceStatusRequest =
new DescribeInstanceStatusRequest().withInstanceIds(argInstanceID);
DescribeInstanceStatusResult describeInstanceResult = null;
LocalDateTime timeStart = LocalDateTime.now();
InstanceStatus status = null;
int code = 0;
String name = argFromState;
boolean inWantedState = false;
int cnt = 1;
while (!inWantedState && (cnt < 20)) {
describeInstanceResult = amazonEC2Client.describeInstanceStatus(describeInstanceStatusRequest);
int numberOfStatuses = describeInstanceResult.getInstanceStatuses().size();
if (numberOfStatuses > 0) {
// We can do this because we requested details of PARTICULAR instance
status = describeInstanceResult.getInstanceStatuses().get(0);
code = status.getInstanceState().getCode();
name = status.getInstanceState().getName();
inWantedState = name.equals(argToState);
} else {
// When instance is terminated, it does not show in the list. That is how we know it is terminated.
if ("terminated".equals(argToState)) {
inWantedState = true;
LOGGER.info("Instance terminated.");
} else {
LOGGER.info("Instance not in the list. It should be.");
}
}
if (!inWantedState) { // status may have just changed...
System.out.println(cnt + "(" + code + "/" + name + ") .. waiting " + (2 * cnt) + "sec for next try.");
try {
Thread.sleep(2 * cnt * 1000);
} catch (InterruptedException ex) {
Logger.getLogger(EC2Wrapper.class.getName()).log(Level.SEVERE, null, ex);
}
} // if
++cnt;
} // while
LocalDateTime timeEnd = LocalDateTime.now();
Duration duration = Duration.between(timeStart, timeEnd);
int secs = (int) duration.getSeconds();
if (secs >= 60) {
LOGGER.log(Level.INFO, "From `{0}` to `{1}` in {2} min.",
new Object[]{argFromState, argToState, secs / 60});
} else {
LOGGER.log(Level.INFO, "From `{0}` to `{1}` in {2} sec.",
new Object[]{argFromState, argToState, secs});
}
} // waitForStateChange() method
If you are talking about maintaining status then as we did in Async task .. setting Cancel(true) will fire a callback with new status.we can accesss the same callback like this in doinBackground().
while(getStatus() == AsyncTask.Status.RUNNING){
// Do ur code ..
}
value returned by getStatus is update with new value, courtesy cancel(true)
Same can be achieved in your code, It would be helpful if some snippet is posted here.

Can only play one sound clip at a time

I wrote a custom sound system for my game, but if two sounds are requested to play within a few ms of eachother only one sound clip will play.
I tried running the playback on a new thread like this but it did not work.
No exceptions are being thrown, it just wont play both sounds.
Thread one = new Thread() {
public void run() {
try {
CustomSound.playSound(id, loop, dist);
} catch (Exception e) {
e.printStackTrace();
}
}
};
Here is the sound player class
public class CustomSound {
/*
* Directory of your sound files
* format is WAV
*/
private static final String DIRECTORY = sign.signlink.findcachedir()+"audio/effects/";
/*
* Current volume state
* 36 chosen for default 50% volume state
*/
public static float settingModifier = 70f;
/*
* Current volume state
*/
public static boolean isMuted;
/*
* Clips
*/
private static Clip[] clipIndex = null;
/*
* Get number of files in directory
*/
private static final int getDirectoryLength() {
return new File(DIRECTORY).list().length;
}
/**
* Loads the sound clips into memory
* during startup to prevent lag if loading
* them during runtime.
**/
public static void preloadSounds() {
clipIndex = new Clip[getDirectoryLength()];
int counter = 0;
for (int i = 0; i < clipIndex.length; i++) {
try {
File f = new File(DIRECTORY+"sound "+i+".wav");
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(f);
clipIndex[i] = AudioSystem.getClip();
clipIndex[i].open(audioInputStream);
counter++;
} catch (MalformedURLException e) {
System.out.println("Sound effect not found: "+i);
e.printStackTrace();
return;
} catch (UnsupportedAudioFileException e) {
System.out.println("Unsupported format for sound: "+i);
return;
} catch (LineUnavailableException e) {
e.printStackTrace();
return;
} catch (Exception e) {
e.printStackTrace();
return;
}
}
System.out.println("Succesfully loaded: "+counter+" custom sound clips.");
}
/**
* Plays a sound
* #param soundID - The ID of the sound
* #param loop - How many times to loop this sound
* #param distanceFromSource - The distance from the source in tiles
*/
public static void playSound(final int soundID, int loop, int distanceFromSource) {
try {
if (!isMuted) {
clipIndex[soundID].setFramePosition(0);
applyVolumeSetting(clipIndex[soundID], getDistanceModifier(distanceFromSource)*settingModifier);
if (loop == 1 || loop == 0) {
clipIndex[soundID].start();
} else {
clipIndex[soundID].loop(loop);
}
/* shows how to close line when clip is finished playing
clipIndex[soundID].addLineListener(new LineListener() {
public void update(LineEvent myLineEvent) {
if (myLineEvent.getType() == LineEvent.Type.STOP)
clipIndex[soundID].close();
}
});
*/
}
} catch (Exception e) {
System.out.println("Error please report: ");
e.printStackTrace();
}
}
/**
* Applies volume setting to the clip
* #param line - the Clip to adjust volume setting for
* #param volume - the volume percentage (0-100)
* #return - the volume with applied setting
*/
public static float applyVolumeSetting(Clip line, double volume) {
//System.out.println("Modifying volume to "+volume);
if (volume > 100.0) volume = 100.0;
if (volume >= 0.0) {
FloatControl ctrl = null;
try {
ctrl = (FloatControl)(line.getControl(FloatControl.Type.MASTER_GAIN));
} catch (IllegalArgumentException iax1) {
try {
ctrl = (FloatControl)(line.getControl(FloatControl.Type.VOLUME));
} catch (IllegalArgumentException iax2) {
System.out.println("Controls.setVolume() not supported.");
return -1;
}
}
float minimum = ctrl.getMinimum();
float maximum = ctrl.getMaximum();
float newValue = (float)(minimum + volume * (maximum - minimum) / 100.0F);
//System.out.println("System min: " + minimum);
//System.out.println("System max: " + maximum);
if (newValue <= ctrl.getMinimum())
newValue = ctrl.getMinimum();
if (newValue >= ctrl.getMaximum())
newValue = ctrl.getMaximum();
ctrl.setValue(newValue);
//System.out.println("Setting modifier = " + volume);
//System.out.println("New value = " + newValue);
return newValue;
}
return -1;
}
/**
* Calculates tile distance modifier
* #param tileDistance - distance in tiles from source
* #return - the distance modifier
*/
public static float getDistanceModifier(int tileDistance) {
if (tileDistance <= 0) {
tileDistance = 0;
}
if (tileDistance >= 10) {
tileDistance = 10;
}
float distanceModifier = 0;
if (tileDistance == 10)
distanceModifier = 0.40f;
if (tileDistance == 9)
distanceModifier = 0.55f;
if (tileDistance == 8)
distanceModifier = 0.60f;
if (tileDistance == 7)
distanceModifier = 0.65f;
if (tileDistance == 6)
distanceModifier = 0.70f;
if (tileDistance == 5)
distanceModifier = 0.75f;
if (tileDistance == 4)
distanceModifier = 0.80f;
if (tileDistance == 3)
distanceModifier = 0.85f;
if (tileDistance == 2)
distanceModifier = 0.90f;
if (tileDistance == 1)
distanceModifier = 0.95f;
if (tileDistance == 0)
distanceModifier = 1.00f;
return distanceModifier;
}
}
When I tested your code on my Windows machine, I had no problems playing two different sounds in short succession:
public static void main(String[] args) throws Exception {
CustomSound.preloadSounds();
CustomSound.playSound(0, 0, 0);
CustomSound.playSound(1, 0, 0);
Thread.sleep(5000);
}
Note however, that DataLine#start() is an asynchronous call. That could be related to your problem.
Also, according to the documentation of DataLine#start(),
If invoked on a line that is already running, this method does nothing.
If that is your problem, and you want to play the same sound twice simultaneously, one possible solution would be to get another Clip instance that plays the same sound and start that.
However I'm no expert at Java's sound API so there might a more efficient way.

Circular ShortBuffer for Audio in Java

I'm implementing an audio track class and I'm in need of a good circular buffer implementation. I'm using shorts for my audio samples, so I would prefer to use a ShortBuffer class for the actual buffer. This track will need to be thread-safe but I can guarantee that only one thread will read and another will write on the track.
My current implementation looks like this (it doesn't handle wrapping).
public class Track {
//sample rate 44100, 2 channels with room for 4 seconds
private volatile ShortBuffer buffer = ShortBuffer.allocate((44100 * 2) * 4);
//keep count of the samples in the buffer
private AtomicInteger count = new AtomicInteger(0);
private ReentrantLock lock = new ReentrantLock(true);
private int readPosition = 0;
public int getSampleCount() {
int i = count.get();
return i > 0 ? i / 2 : 0;
}
public short[] getSamples(int sampleCount) {
short[] samples = new short[sampleCount];
try {
lock.tryLock(10, TimeUnit.MILLISECONDS);
int writePosition = buffer.position();
buffer.position(readPosition);
buffer.get(samples);
//set new read position
readPosition = buffer.position();
// set back to write position
buffer.position(writePosition);
count.addAndGet(-sampleCount);
} catch (InterruptedException e) {
System.err.println("Exception getting samples" + e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return samples;
}
public void pushSamples(short[] samples) {
try {
lock.tryLock(10, TimeUnit.MILLISECONDS);
buffer.put(samples);
count.addAndGet(samples.length);
} catch (InterruptedException e) {
System.err.println("Exception getting samples" + e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
Here's the solution that I have come up with http://pastebin.com/2St01Wzf I decided it was easier to use a head and tail property with a short array, instead of just the read position with a ShortBuffer. I also took an idea from the Java collections classes to detect when the buffer is full. Here is the source, just in case the pastebin disappears:
public class Track {
private static Logger log = LoggerFactory.getLogger(Track.class);
private final long id = System.nanoTime();
// number of channels
private int channelCount;
// maximum seconds to buffer
private int bufferedSeconds = 5;
private AtomicInteger count = new AtomicInteger(0);
private ReentrantLock lock;
private volatile short[] buffer;
private int capacity = 0;
private int head = 0;
private int tail = 0;
public Track(int samplingRate, int channelCount) {
// set the number of channels
this.channelCount = channelCount;
// size the buffer
capacity = (samplingRate * channelCount) * bufferedSeconds;
buffer = new short[capacity];
// use a "fair" lock
lock = new ReentrantLock(true);
}
/**
* Returns the number of samples currently in the buffer.
*
* #return
*/
public int getSamplesCount() {
int i = count.get();
return i > 0 ? i / channelCount : 0;
}
/**
* Removes and returns the next sample in the buffer.
*
* #return single sample or null if a buffer underflow occurs
*/
public Short remove() {
Short sample = null;
if (count.get() > 0) {
// decrement sample counter
count.addAndGet(-1);
// reposition the head
head = (head + 1) % capacity;
// get the sample at the head
sample = buffer[head];
} else {
log.debug("Buffer underflow");
}
return sample;
}
/**
* Adds a sample to the buffer.
*
* #param sample
* #return true if added successfully and false otherwise
*/
public boolean add(short sample) {
boolean result = false;
if ((count.get() + 1) < capacity) {
// increment sample counter
count.addAndGet(1);
// reposition the tail
tail = (tail + 1) % capacity;
// add the sample to the tail
buffer[tail] = sample;
// added!
result = true;
} else {
log.debug("Buffer overflow");
}
return result;
}
/**
* Offers the samples for addition to the buffer, if there is enough capacity to
* contain them they will be added.
*
* #param samples
* #return true if the samples can be added and false otherwise
*/
public boolean offer(short[] samples) {
boolean result = false;
if ((count.get() + samples.length) <= capacity) {
pushSamples(samples);
result = true;
}
return result;
}
/**
* Adds an array of samples to the buffer.
*
* #param samples
*/
public void pushSamples(short[] samples) {
log.trace("[{}] pushSamples - count: {}", id, samples.length);
try {
lock.tryLock(10, TimeUnit.MILLISECONDS);
for (short sample : samples) {
log.trace("Position at write: {}", tail);
if (!add(sample)) {
log.warn("Sample could not be added");
break;
}
}
} catch (InterruptedException e) {
log.warn("Exception getting samples", e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* Returns a single from the buffer.
*
* #return
*/
public Short popSample(int channel) {
log.trace("[{}] popSample - channel: {}", id, channel);
Short sample = null;
if (channel < channelCount) {
log.trace("Position at read: {}", head);
try {
lock.tryLock(10, TimeUnit.MILLISECONDS);
sample = remove();
} catch (InterruptedException e) {
log.warn("Exception getting sample", e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
return sample;
}
}

Categories