@nsigma: Lag? Largely due to GUI laziness on my part. (Shot myself in the foot?)
Slightly improved demo is now up: http://www.hexara.com/VSL/AudioMixerDemoWarOfWorlds.htm
For one, Java JButtons don’t send events until the MouseUp occurs. So the sounds will naturally seem to fall behind the clicks. Maybe I should create MouseDown sensitive “touchpads.” (:cranky: Have…other…priorities…)
I admit I used a single ActionListener, and a chain of string comparisons to interpret the ActionCommand strings. I’ve changed this to individual, anonymous ActionListeners. The RayGun has a built-in ramp-up time to prevent clicks. I sharpened that envelope by a factor of 5. But that ramp-up could still be perceived as lag. Also, since I run them (and the machine gun) on new threads, I should probably use a thread pool, which could also reduce latency. The Gun.wav could probably be edited a bit tighter. Silent samples at the front end could be perceived as lag, especially at the slower playback rates of the bigger bombs. There’s a tiny bit of distortion near the very end of the cheap, free gunshot sample as well.
All in all, my point is that it is dubious to judge lag based solely on a web demo. One doesn’t know what the programmer has done via the GUI layer. Better is to ask: is it in the ball park, and thus worth taking a more serious look?
+++++++++++++++++++++++++++++++++++++++++++++++
Here’s what the coding looks like, as far as an example of api use. The AudioMixer is turned on via a play(mixerLine) command. The null means: use the default output line. One could specify a particular output line here, for example if needed for Linux. Three MixerTracks are specified for the AudioMixer: a gunshot ClipTrack and two RayGuns. (Both ClipTrack and RayGun implement the MixerTrack interface. Yes, all the bombs & guns are on the same MixerTrack.) By passing the AudioMixer as a parameter in their creation, the tracks are created automatically.
audioMixer = new AudioMixer();
audioMixer.play(null);
audioMixer.setMasterVolume(0.65);
// create Mixer tracks, one for gun and two RayGuns
try {
gun = new ClipTrack(audioMixer,
"audio/GunshotIndoor2.wav", true);
} catch (UnsupportedAudioFileException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ray1 = new RayGun(audioMixer, 500, 0.6f, 12, 5);
ray2 = new RayGun(audioMixer, 1250, 0.5f, 50, 4);
To play the various is also pretty straightforward. The ClipTrack play() optionally take a factor that controls playback rate. For example play(0.5) plays at half speed, and play(2) plays back at double speed. RayGuns are ‘continuous’ so I use a start() and stop(). I didn’t explain the RayGun parameters here because I haven’t settled on the specifics of that class. [Parameter list: AudioMixer, frequency (in Hertz), volume, LFO modulation rate (in Hertz), modulation depth (in wavetable samples :P)]
// ActionListeners added to JButtons:
bigBomb.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e) {
gun.setVolume(0.8f);
gun.play(0.24);
}
});
mediumBomb.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e) {
gun.setVolume(0.7f);
gun.play(0.30);
}
});
smallBomb.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e) {
gun.setVolume(0.6f);
gun.play(0.38);
}
});
gunshot.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e) {
gun.setVolume(0.5f);
gun.play();
}
});
machineGun.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e) {
PlayMachineGun pmg = new PlayMachineGun();
Thread t = new Thread(pmg);
t.start();
}
}
);
rayGun1.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
DeathRay dr = new DeathRay();
Thread t = new Thread(dr);
t.start();
}
});
rayGun2.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent arg0)
{
RayGunBlast rgb = new RayGunBlast();
Thread t = new Thread(rgb);
t.start();
}
});
class PlayMachineGun implements Runnable
{
@Override
public void run()
{
gun.setVolume(0.6f);
for (int i = 0; i < 5; i++)
{
gun.play(1.25);
try {
Thread.sleep(88);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class DeathRay implements Runnable
{
@Override
public void run()
{
ray1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ray1.stop();
}
}
class RayGunBlast implements Runnable
{
@Override
public void run()
{
ray2.start();
try {
Thread.sleep(350);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ray2.stop();
}
}
Notice: no need to deal with SourceDataLines or AudioInputStreams, etc. And with this ClipTrack, one can leverage a single .wav resource to a considerable extent.
Meanwhile, I DO look forward to the appearance of other mixers that have been promised and are in the works. It will be awesome to have multiple options.