I am relatively new to programming, and to help myself I am making a personal project.
I am using Javafx to build drum machine, which allowing the user to program a sequence of beats.
I have constructed sets of rows which each will function as a programmable beat sequencer for each corresponding instrument, with each row consisting of 16 buttons. if that button is pressed, the button is activated, and it will produce the instrument's sound when the loop passes through that point.
for reference this piece of kit is similar to what i wish to construct :
https://images-na.ssl-images-amazon.com/images/I/81RctDCP38L._AC_SL1500_.jpg
Each button is assigned to a hashmap; the Key is an integer from 0-16, while the value is the button's characteristics itself.
The drum machine loops after 4 bars/ 16 buttons.
To trigger the event and to cause the instrument to play, the time (as a fraction of all buttons/16) will match the key of a button. once this occurs, the sound plays. the method to do this is below:
public void beatState(Button button, String filename) {
EventHandler handler = new EventHandler() {
#Override
public void handle(Event event) {
soundGeneration sound = new soundGeneration(filename);
// if the corresponding buttons key (on a range of 0-16) matches the time as a fraction (0-16)
// and if the button text is on (activated by clicking the pad)
if (button.equals(map.get(time.timeToFraction())) & button.getText().equals("On")) {
// plays the file
sound.play();
// when duration of animation is set lower the filename prints more frequently
// or sometimes not printed/sound played when the duration is higher
System.out.println(filename);
}
}
};
// as i increase the duration of the animation , the program becomes both slower but returns
// both the sound file and prints the filename more often
Timeline animationButton = new Timeline(new KeyFrame(Duration.millis(100), handler));
animationButton.setCycleCount(Timeline.INDEFINITE);
animationButton.play();
}
I'll next provide the time element:
public Integer timeToFraction(){
labelFormat();
new AnimationTimer() {
#Override
public void handle(long now) {
// time elapsed since program execution
double elapsedMillis = (System.currentTimeMillis() - startTime);
// converts to long
// multiplies the value to privide a fraction
long numerator = (long) ((elapsedMillis/1000)*8.33);
// value below denominates the time it takes to travel 4 beats at 125 beats per minute
double denominator = 1.92;
// converts below to show as a fraction
long denominatorToBeat =(long) Math.round(denominator * 8.3);
// if the elapsed time raises over 16
// resets numerator
if (numerator> denominatorToBeat) {
startTime = System.currentTimeMillis();
elapsedMillis = (System.currentTimeMillis() - startTime);
}
// converts from long to to allow for hashmap matching against button position
fractionTime = (int) numerator;
}
}.start();
return fractionTime;
}
When the values match up the beat plays, achieving my aim; however, it plays multiple times, and irregular in relation to the beat.
I assume that the animation timer repeat value in milliseconds is what causes this; I decrease it, there are more unwanted repeated sounds. I increase it and notes sometimes are not counted due to the animation passing over the value before it triggers.
I want the code to trigger the sound file when it passes over the button, more specifically when the Integer value of the hashmap the button corresponds to matches the time fraction.
I have spent hours researching and reading the documentation and for an easy problem it is becoming incredibly difficult to work around. Given that this project is seen in a multitude of portfolios and music development software i am sure there is a simple fix to my issue.
Thanks for taking the time.
Your approach in general is wrong, as you are abusing animation timers to do something not related to the GUI. You have to decouple the logic from the GUI.
I.e. The object which controls the timing and playing the sounds should have nothing to do with the buttons or any GUI. It should have some methods to set / clear a sound at particular interval, and then expose its state using properties, e.g. an ObjectProperty with some class describing the current tones being played.
The buttons should call the methods to set / clear tones, and you can add a change listener to the ObjectProperty to update the buttons appearance based on the state.
Related
I am creating a device where you can do video game bike racing by putting your bicycle on a trainer and hooking it up to your tablet/phone, which will count the pulses from a reed switch or hall effect sensor and calculate your speed, distance, etc in the same manner as a regular bike computer, and use that to move your avatar in the game world. It's written in Java with libGDX.
I'm stuck trying to get the digital bike to coast to a stop. If you are going a given speed, such that the time between pulses is normally 100ms, then if we check the time delta and it's been 200ms since the last click, we know you can't be going more than half of your former speed, and we should adjust down accordingly every time we update your speed, eventually passing some threshold where we decide you've effectively stopped.
I want the speed to decay along a roughly logarithmic curve so that hopefully I can approximate the speed envelope that your bike would have out on pavement. I also need to be smart about estimating the distance you've traveled since the last click, so that your distance doesn't jump ahead if we suddenly get a click.
How can I accomplish getting the bike to coast to a stop, and how should I go about positioning the bike in the game world with the uncertainty of how far we have to go until the next click? I have scoured the Internet looking for examples of code for a bicycle computer, and haven't found any yet, let alone any that have the added restriction that they need to be able to render their current position in the game world.
Here's the code:
The click function runs every time we get another pulse from the bike sensor:
public void click() {
currentTime = System.currentTimeMillis();
int delta = (int) (currentTime - lastClick);
// If we have less than N samples, just add it to the list.
// Otherwise pop one off and fill it in with the new value.
if (samples.size() == SAMPLE_RESOLUTION) {
samples.remove(0);
}
samples.add(delta);
clicks += 1;
lastClick = currentTime;
}
And the averageSamples function averages the last N samples to smooth out the speed. It's just a simple average right now, but later I intend on making it weighted such that new data is weighted more than old data.
public double averageSamples() {
Integer sum = 0;
if (!samples.isEmpty()) {
for (Integer sample : samples) {
sum += sample;
}
return wheelCircumference / (sum.doubleValue() / samples.size());
}
return sum.doubleValue();
}
And finally, the update function runs every frame of the game, so about once every 60th of a second. It's supposed to calculate your speed and distance, guessing how far you've gone since the last click based on the amount of time that's passed since then:
public void update() {
double newSpeed;
currentTime = System.currentTimeMillis();
int delta = (int) (currentTime - lastClick);
// The below line needs to adjust your speed downward if it's been too long since the last click. We're hoping for a smooth curve on the way to coasting to a stop.
newSpeed = averageSamples();
elapsedTime = currentTime - startTime;
instSpeed = newSpeed;
avgSpeed = (avgSpeed + instSpeed) / 2;
maxSpeed = Math.max(newSpeed,instSpeed);
/* This line needs to guess how far you've gone since the last click. Right now I'm using this value directly to draw the bike at a certain place in the game world, so we need to do this in a way that if we suddenly get a click, you don't end up jumping forward. */
distance = (long) (clicks * wheelCircumference);
}
I am using LibGdx to develop an android game . I have implemented the gesture listener class in my InputHandler class . Now in the tap method I have to implement two features , short jump and long jump on single tap and double tap respectively. When I try to implement it using the count value of the function , the problem is when I double tap the screen the count value firstly becomes 1 and then 2 so it does not go into the second if statement and and the feature of short jump occurs. So how to differentiate single jump from double jump? Below is the code
#Override
public boolean tap(float x, float y, int count, int button) {
// TODO Auto-generated method stub
if(count==1)
{
//feature 1
}
if(count==2)
{
//feature 2
}
return true;
}
two solutions comes to my mind:
Use delay technique which would come with following steps:
when tap-count is 2 : trigger action for double tap
when tap-count is 1 : wait some time, if not tapped second time trigger action for single tap
code would be something like:
if(count==1)
{
if(wait) //wait is a boolean tells if it was tapped once already
{
if(getCurrentTime() - startTime > interval) //getCurrentTime() return current time or frame count or something
{
wait = false;
//feature 1
}
}
else
{
wait = true;
startTime = getCurrentTime(); //start time keeps the time when tapped first one
}
}
if(count==2)
{
//feature 2
}
return true;
}
the problem I see here is firstly "how to choose long enough interval for waiting for secon tap?" - if you will choose to short it will be impossible to make double tap, if too long there will be a lag and user will see that the character is not jumping directly after tap
Divide jump for a pieces and trigger them depends on tap count
And it depends on your "jumping" mechanism. If it be something like:
A. add force to jump
B. keep adding some force for some time to keep object in the air
C. avoid adding force to take object down back to ground
you could modify time to keep object in the air longer. If your "jumping mechanism" is more like impuls adding force (in upper example it would be variant without B point) you could add the force in A again for a some time and if user tapped second time before this interval ends keep on adding it just longer.
This solution eliminates lag problem but depends on "jumping mechanism" you choose.
Regards,
MichaĆ
I'm making fast forward button in my Mp3 player. I wrote already code for this, but have problem how to implement timer to jump 5% forward? I mean when I press button timer should jump 5% forward of total long song. This is my fastForward method.
public void FastForward(){
try {
//songTotalLength = fis.available();
fis.skip((long) ((songTotalLength * 0.05)));
} catch (IOException e) {
e.printStackTrace();
}
}
And here is the button method:
private void jButton3ActionPerformed(ActionEvent e) {
mp3.FastForward();
if(mp3.player.isComplete()){
bar = 100;
}
jProgressBar1.setValue((int)bar);
bar+=5;
}
And this one is for timer:
private void setTime(float t) {
int mili = (int) (t / 1000);
int sec = (mili / 1000) % 60;
int min = (mili / 1000) / 60;
start.set(Calendar.MINUTE, 0);
start.set(Calendar.SECOND, 0);
end.set(Calendar.MINUTE, 0 + min);
end.set(Calendar.SECOND, 0 + sec);
timer = new javax.swing.Timer(1000, new TimerListener());
percent = (float)100/(min*60+sec);
}
You're already redundantly tracking progress in two variables fis and bar. For the sake of good design, use one of those to determine elapsed time rather than using yet another variable for the same purpose.
but have problem how to implement timer to jump 5% forward?
You seem to have mistaken the purpose of a Timer. According to the Timer class documentation, it:
Fires one or more ActionEvents at specified intervals.
So the timer is not meant to keep track of how much time has elapsed. It will simply invoke the actionPerformed(ActionEvent) method of your TimerListener once per second (every 1000 milliseconds) as you have it configured.
For a more complete answer, please post the code for your TimerListener class.
Notes
It seems that your setTime(float) method is meant to be called repeatedly, so this method should not be initializing the timer variable. Rather initialize the timer once and leave it alone to do its job.
I'm assuming you intended the supplied float parameter t to represent microseconds.
The float data type has only 7 digits of precision. This could be fine since you're interested only in minutes and seconds, otherwise float is only good for up to about four months of seconds before losing accuracy.
It seems like you wanted your button click handler to do this (increment bar sooner):
private void jButton3ActionPerformed(ActionEvent e) {
mp3.FastForward();
bar+=5; // increment bar before checking complete, and before setting progress
if(mp3.player.isComplete()){
bar = 100;
}
jProgressBar1.setValue((int)bar);
}
As the question says, here's the code.
What I want to do basically is I have a list of array, says "myArray". This array contains stored time points in millisecond. Next thing is I want to call a method based on those time points. So, I need to run a time counter from 0 while the music is playing, compare the time counter with the time points in the array progressively (from first array index until finished).
The first method "timeCounter" is a method to start a time counter while a music is playing through MediaPlayer.
public void timeCounter(){
Runnable myRunnable = new Runnable(){
#Override
public void run(){
while(musicPlaying){
startCounter(arrayPos);
}
}
};
Thread musicPlayer = new Thread(myRunnable);
musicPlayer.start();
}
This is where the comparison of time counter and time in the array takes place.
public void startCounter(List<Long> array){
elapsedTime = System.currentTimeMillis() - timeStart;
if(elapsedTime == array.get(arrayPos)){
arrayPos += 1; //Continue comparing with the next number in the array
Log.v("DEBUG", "TIME MATCHES!");
if(arrayPos>= array.size()){
arrayPos= 0; //Prevent out of array index
}
}
}
I have the code running correctly and the result from the LogCat is correct.
Now the problem is sometimes the Thread is not running until the music finished playing (I have set onCompletionListener and set "musicPlaying" to false when music finished). The stopping point is totally random, sometimes when it's just started, or around 50%, or even when it's near to completion. Sometimes it doesn't even start!
I've been trying to figure out why but I couldn't find any info about Thread stopping halfway or such in anywhere. Thanks in advance! =)
If musicPlaying is modified and read between threads, make sure it is declared volatile.
I suspect your if(elapsedTime == array.get(arrayPos)) should be if(elapsedTime >= array.get(arrayPos)).
You need to remember that the millisecond timer does not necessarily have a one millisecond tick. On Windows, for example, the returned value will only change every 15 ms or so. You may therefore completely miss a particular time and therefore never get past that entry in your list.
I am making a little game in Processing which is similar to those Guitar Hero style games and I am trying to do 2 things:
When the game loads, stop the time from moving
During the game, allow for Pause functionality
Now, I know I cant stop the time since the millis() returns the milliseconds since the application launched, so my timer will need to be millis() - millis() at the start to equal zero, so when the user presses START, they can obviously start at the start. The game reads a file at the start, similar to a subtitles file, that has the note to be played and the time in milliseconds that it should appear on screen.
My problem is, when I pause the game, the timer keeps going and when I unpause the game, all the notes get "bunched up" due to my logic, as you'll see from my code.
Can someone suggest a better algorithm than the one I'm using? Its late and I've been working on this all day and night. I think the problem is with the for() below:
public void draw()
{
if (gameInProgress)
{
currentTimerValue = millis(); // Update the timer with the current milliseconds
// Check to see if the note times falls between the current time, or since the last loop (difficult to match exact millisecond)
for(int i=0 ; i<songNotes.length ; i++)
{
if( songNotes[i].getStartTime() > previousTimerValue && songNotes[i].getStartTime() <=currentTimerValue)
notes.add(songNotes[i]);
}
noStroke();
textFont(f,18);
drawButtons(); //Draws coloured buttons relating to Button presses on the controller
drawHighScoreBox(); // Draws high score box up top right
drawLines(); // Draws the strings
moveNotes(); // Moves the notes across from right to left
//Now set the cutoff for oldest note to display
previousTimerValue=currentTimerValue; //Used everytime on the following loop
}
else
{
drawMenu(); // Draw the Main/Pause menu
}
}
NOTE: The boolean gameInProgress is set below when the users presses the pause button, eg "P", and songNotes is an array of objects of type Note that I wrote myself. It has 2 member variables, noteToBePlayed and timeToBePlayed. The method getStartTime()returns timeToBePlayed which is a millisecond value.
Any help is appreciated.
Thanks
How about having another integer to store time when you pause and use that to offset the game timer ?
So, in 'gameInProgress' mode you update currentTimerValue and previousTimerValue and in 'paused/menu' mode you update a pausedTimerValue, which you use to offset the 'currentTimerValue'. I hope this makes sense, it sounds more complicated in words, here's what I mean:
boolean gameInProgress = true;
int currentTimerValue,previousTimerValue,pausedTimerValue;
void setup(){
}
void draw(){
if(gameInProgress){
currentTimerValue = millis()-pausedTimerValue;
println("currentTimerValue: " + currentTimerValue + " previousTimerValue: " + previousTimerValue);
previousTimerValue=currentTimerValue;
}else{
pausedTimerValue = millis()-currentTimerValue;
}
}
void mousePressed(){
gameInProgress = !gameInProgress;
println("paused: " + (gameInProgress ? "NO" : "YES"));
}
Click the sketch to toggle modes and look in the console for times. You'll notice that you only loose a few millis between toggles, which is acceptable.
Use not system timer but special timer class with pause functionality. I'm sure it is not hard to implement such class by yourself. I know that java has Timer class but unfortunately it not support pause functionality.