Reputation: 28905
I'm trying to make a program that will generate a sound with a specified loudness for one second based on a parameter. I have included the code below. In the setUpSound()
function, there is a line "control.setValue(integer)
" which controls the volumne. For example, if the value is -30, there will be a really soft beep for one second. If the value is 20, there will be a really loud noise for one second.
On the cases when the noise is soft (when the paramater is in the -30 to -10 range), there is an initial loud noise that lasts about 100 miliseconds or so, and then the soft regular sound comes on. It almost sounds like the sound card on my computer beings startled for a fraction of a second before it plays the regular normal sound. Does that make sense? You can try it on your machine as well. I'm using a MacBook Pro running Lion.
I'm trying to figure out why Java does this, and how to resolve it?
import java.io.ByteArrayInputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
FloatControl control;
public static void main(String[] args) {
Test test = new Test();
test.setUpSound();
test.loopSound(true);
try {
Thread.sleep(1000);
} catch(Exception e) {
}
test.loopSound(false);
}
Clip clip;
public void setUpSound() {
/** Volume */
try {
generateTone();
control = (FloatControl)
clip.getControl( FloatControl.Type.MASTER_GAIN );
System.out.println("min volume: " + control.getMinimum());
System.out.println("max volume " + control.getMaximum());
System.out.println("end of sounds");
control.setValue(-40);
} catch(Exception e) {
e.printStackTrace();
}
}
public void loopSound(boolean commence) {
if ( commence ) {
clip.setFramePosition(0);
clip.loop( Clip.LOOP_CONTINUOUSLY );
} else {
clip.stop();
}
}
/** Generates a tone, and assigns it to the Clip. */
public void generateTone()
throws LineUnavailableException {
if ( clip!=null ) {
clip.stop();
clip.close();
} else {
clip = AudioSystem.getClip();
}
boolean addHarmonic = true;
int intSR = 10025;
int intFPW = 20;
System.out.println("sampleRate = " + intSR);
System.out.println("framesPerWavelength = " + intFPW);
float sampleRate = (float)intSR;
// oddly, the sound does not loop well for less than
// around 5 or so, wavelengths
int wavelengths = 5;
byte[] buf = new byte[2*intFPW*wavelengths];
AudioFormat af = new AudioFormat(
sampleRate,
8, // sample size in bits
2, // channels
true, // signed
false // bigendian
);
for(int i=0; i<intFPW*wavelengths; i++){
double angle = ((float)(i*2)/((float)intFPW))*(Math.PI);
buf[i*2]=getByteValue(angle);
if(addHarmonic) {
buf[(i*2)+1]=getByteValue(2*angle);
} else {
buf[(i*2)+1] = buf[i*2];
}
}
try {
byte[] b = buf;
AudioInputStream ais = new AudioInputStream(
new ByteArrayInputStream(b),
af,
buf.length/2 );
clip.open( ais );
} catch(Exception e) {
e.printStackTrace();
}
}
/** Provides the byte value for this point in the sinusoidal wave. */
private static byte getByteValue(double angle) {
int maxVol = 127;
return (new Integer(
(int)Math.round(
Math.sin(angle)*maxVol))).
byteValue();
}
}
Upvotes: 1
Views: 689
Reputation: 168825
..how to resolve it?
Leave the master volume alone and adjust the volume using another parameter to the getByteValue()
method (return smaller byte values for soft sounds).
..use another parameter..
I.E.
- Change getByteValue()
to getByteValue(float scale)
.
- throw
an IllegalArgumentException
if the range is outside 0.0f
- 1.0f
.
- Multiply the value of the signal at that moment by the scale
value.
If a value of 0.5f is provided for scale
, the sound will be less loud than if the number is 1.0f.
Upvotes: 1