Hi all,
I’m starting to work on a music/metronome application in
Java and I’m running into some problems with the timing and speed.
For
testing purposes I’m trying to play two sine wave tones at the same time at regular intervals, but instead they play in sync for a few beats and then slightly out of sync for a few beats and then back in sync again for a few beats.
From researching good metronome programming, I found that Thread.sleep() is horrible for timing, so I completely avoided that and went with checking System.nanoTime() to determine when the sounds should play.
I’m using AudioSystem’s SourceDataLine for my audio player and I’m using a
thread for each tone that constantly polls System.nanoTime() in order to determine when the sound should play. I create a new SourceDataLine and delete the previous one each time a sound plays, because the volume fluctuates if I leave the line open and keep playing sounds on the same line. I create the player before polling nanoTime() so that the player is already created and all it has to do is play the sound when it is time.
In theory this seemed like a good method for getting each sound to play on time, but it’s not working correctly.
At the moment this is just a simple test in Java, but my goal is to create my app on mobile devices (Android, iOS, Windows Phone, etc)...however my current method isn’t even keeping perfect time on a PC, so I’m worried that certain mobile devices with limited resources will have even more timing problems. I will also be adding more sounds to it to create more complex rhythms, so it needs to be able to handle multiple sounds going simultaneously without sounds lagging.
Another problem I’m having is that the max tempo is controlled by the length of the tone since the tones don’t overlap each other. I tried adding additional threads so that every tone that played would get its own thread...but that really screwed up the timing, so I took it out. I would like to have a way to overlap the previous sound to allow for much higher tempos.
I posted this question on StackOverflow where I got one reply and my response back explains why I went this direction instead of preloading a larger buffer (which is what they recommended). In short, I did try the buffer method first, but I want to also update a “beat counter” visual display and there was no way to know when the hardware was actually playing the sounds from the buffer. I mentioned that on StackOverflow and I also asked a couple more questions regarding the buffer method, but I haven’t received any more responses.
http://stackoverflow.com/questions/24110247/java-audio-metronome-timing-and-speed-problems
Any help getting these timing and speed issues straightened out would be greatly appreciated! Thanks.
Here is my code...
SoundTest.java
SoundElement.java
EDIT: Sorry, I had some test print statements in my code that I forgot to remove before posting.