1

The problem I have is that the decibals of the audiofile only changes from the initial value of programVolume (the variable) but I have a JSlider that alters the the value of the variable;however, it does not alter the decibals of the file. What do I add/change to make sure the decibal changes based off the values set to the variable from the JSlider.

  File fileSong = new File (filePath); 
  AudioInputStream input = AudioSystem.getAudioInputStream(fileSong); 
  clip = AudioSystem.getClip(); 
  clip.open(input); 
  FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
  gainControl.setValue(programVolume);
  clip.start();

EDIT:

I have added the following code:

  File fileSong = new File (filePath); 
  AudioInputStream input = AudioSystem.getAudioInputStream(fileSong);
  clip = AudioSystem.getClip(); 
  clip.open(input); 

  FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
  float range = gainControl.getMaximum() - gainControl.getMinimum();
  float gain = (range * programVolume) + gainControl.getMinimum();
  gainControl.setValue(gain);

For my JSlider (which is in the method ChangeEvent e)

    programVolume =  (float)volume.getValue() / 100; 

I am still facing the same problem. The new values recieved from the JSlider do not change the volume. Only the initial value of programVolume alters the sound.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • Read the java.swing doc about that – midugh Jan 01 '20 at 05:03
  • @R.LM I already setup the JSlider the problem I have right now is changing the volume of the audio file while it is already playing. The slider is setting new values to the programVolume variable but I am not sure why its not changing the volume based on the new values. – Shahrukh Qureshi Jan 01 '20 at 05:10
  • Files don't have volume. Your system does. https://stackoverflow.com/questions/4964947/set-computer-volume – OneCricketeer Jan 01 '20 at 05:12
  • 1
    I'm pretty sure they do - I could have an audio file with the sounds of pins dropping, (very quiet) and a file with the sounds of pans smashing (very loud). One would obviously be louder than the other. @cricket_007 – FailingCoder Jan 01 '20 at 05:33
  • When you change the decibels of a file it alters the sound that is what I am trying to do but I have no idea what to do. – Shahrukh Qureshi Jan 01 '20 at 05:46
  • 1
    Here you are changing the level of the stream, not of the file itself. – user207421 Jan 01 '20 at 06:22
  • 1
    For better help sooner, [edit] to add a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). Preferably hot-link to a sampled sound file. – Andrew Thompson Jan 01 '20 at 07:33

2 Answers2

2

The first suggestion from @gthanop on ShaleeQureshi's answer is quite important. Reloading a Clip is very inefficient. The entire Clip has to be reloaded before it can be restarted.

Often there are issues with Controls, as these are dependent on native code that interacts with the different systems. Controls that are implemented on one PC may not be implemented on another due to features missing on that PC.

Another issue that comes up is that the changes arising from Controls are tied to buffer boundaries. If for example the buffer is a half of a second long, it would take up to a half second for the new value to become operative.

There is no "decibel" control, per se, in an audio file. Instead, the volume arises from the dynamic range of the data values that form the signal. A quiet file may only range from -0.05 to 0.05, where a loud one might be more like -0.5 to 0.5. An audio file may have sections that are loud and others that are soft, but the entirety is still encoded directly as signal values. There is no "volume" control on a .wav file.

Since the Controls provided by Java cannot be depended upon, you might want to check out the github resource AudioCue. It is basically an enhanced Clip, with real time volume controls. The code has a permissive license and is free to use or to inspect for ideas. The basic principle is to store the data in a float[] rather than a Clip, and to play it back via a SourceDataLine while making constant reference to a volume factor that is multiplied against the signal values as they stream.

Phil Freihofner
  • 7,645
  • 1
  • 20
  • 41
  • Glad to see you weigh in, Phil! I was hoping one of the two [top ranked](https://stackoverflow.com/tags/javasound/topusers) ppl in this API provide an answer, and it sure wasn't going to be me! ;) You have once again proved your expertise in both sound manipulation and the Java Sound API. (OP: This IS the BEST answer you are likely to see on this site .. and probably many others besides.) – Andrew Thompson Jan 02 '20 at 10:56
  • 1
    Thanks for the compliments, Andrew! I felt a little bleary when I wrote this, but I think the info is good. It occurs to me that I didn't directly address the specific code being used by the OP, so that is a weakness of the answer, as well as avoided talking about the relationship between signed normalized PCM (-1 to 1) and decibels, which I'm not all that clear on. I haven't used decibels as a unit of measure in any projects. As always, am appreciative of corrections if people have them. TBH, my high numbers are mostly due to persistence, as I'm self-taught and not a DSP engineer. – Phil Freihofner Jan 02 '20 at 18:03
0

I figured it out! Whenever the variable was altered using the JSlider the clip had to be stopped and the point at which it was stopped was to be stored onto a variable. I chose a long and then the clip had to be restarted and when it restarts it uses the new volume the user selected and it begins at the same time it left off at giving the illusion to the user that the volume was altered while the audio-file was playing when it stopped, changed volume, and resumed. The Math.Log stuff is used to calculate/convert the decibels to the volume system we know (0%-100%) or so I think.

  File fileSong = new File (filePath);
  AudioInputStream input = AudioSystem.getAudioInputStream(fileSong); 
  clip = AudioSystem.getClip(); 
  clip.open(input); 
  clip.setMicrosecondPosition(position); 

  FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); 
  float range = (float) (Math.log(userInput) / Math.log(10) * 20); 
  gainControl.setValue(range); 
  clip.start(); 
  • 1
    Hello. As I understood from the [JavaDocs](https://docs.oracle.com/javase/8/docs/api/javax/sound/sampled/DataLine.html) it may be possible to just call *stop* then change the volume and then call *start* again. This way you are avoiding to reopen the audio stream every time the user changes volume (and also setting the position), so I would expect this to be more performant. Try it out. Just a notice. – gthanop Jan 01 '20 at 13:28
  • 1
    There is also a [FloatControl.Type.VOLUME](https://docs.oracle.com/javase/8/docs/api/javax/sound/sampled/FloatControl.Type.html#VOLUME) to check out. It may be just the volume in percentage (so that you don't have to convert from decibels to percentage). – gthanop Jan 01 '20 at 13:33
  • @gthanop that only works if there is no change in the audio but since the volume is being changed we need to reopen the audio file to create a new stream with new volume. - or so I believe – Shahrukh Qureshi Jan 01 '20 at 17:40