MASTER_GAIN control on Clip but not SourceDataLine??

Is this typical? When I play a .wav as a Clip, I have access to a MASTER_GAIN control, and can use this to set the volume. When I play the same .wav as a SourceDataLine, there are no controls exposed whatsoever.

I’m using the getControls() function in both instances, on Clip “currentClip” in the first case and on SourceDataLine “currentLine” in the other. So, I’m pretty sure I’m invoking the controls correctly.

I guess, if I use a SourceDataLine, I’m going to have to scale the values for every data point in each input buffer as I am loading? Dragola. :stuck_out_tongue: Is that common practice?

Consulting “Killer Game Programming in Java,” all the examples with volume controls pertain to Clips. So I guess most games limit themselves to Clip usage?

Why not use Clips myself? Because for my design I want (insist upon) sounds that can recur with possible overlapping. I could preload three or four instances of each and manage them as a pool, but the multiplier effect on RAM needed is worrisome. So, I thought it better to load each one when it is needed, and loading as a SourceDataLine allows for quicker starts than continually reloading new Clips on an as-needed basis. But the SourceDataLine doesn’t have a volume control!?!?

But I may have to head back to some sort of Clips pooling method if decoding OGG/Vorbis turns out to be slow to kick off with SourceDataLine playback.

More conversations with myself. This has been a common occurance in the Audio section for me. I assume that is because most folks here at Java-Gaming are putting their emphasis elsewhere, and that I am trying to do things in a slightly non-normal way by working things out with JavaSound rather than going with an existing system.

OK, so the results are in. I wrote my own volume changer that works directly on the data in the buffer used to read data in from the .wav before it is sent back out to SourceDataLine. And it works like a charm!

Basically, by doing so, one can increment or decrement the volume towards a desired volume every frame! Thus the increment/decrement value can be quite small and a respectable volume changing speed can be obtained without clicks occuring. In my first test, I have the increment set to +/- 0.00001f, where the entire dynamic range is 0.0f to 1.0f. With 44100 fps, the entire dynamic range can be traversed in less than half a second with not a single click. (Haven’t tested for upper limits of speed yet.)

And (bonus): the slight occasional hesitations of the sound cues have disappeared, as far as I can tell. SourceDataLine’s really do start quicker and more reliably than starting a brand new Clip. Makes sense, as Clips are designed to optimize for reuse, not first use.

I must admit it can be quiet in here!

FWIW I am working on Java Sound at the moment. Sampled, not midi. I have written a few simple wrapper classes for Clips and Streams.
I am quite enjoying it! I found the Java Tutorial section on sound pretty good for everything. You can definitely use Volume control on SourceDataLine. Are you following the section “Getting the Controls from the Line” at http://download.oracle.com/javase/tutorial/sound/controls.html? I haven’t yet implemented the controls in my sound “engine” (what a grand term for a few small classes hehe).

If you like I can share my code with you, it is still in a state of active development.

Martin

Nice to meet you!

[quote]You can definitely use Volume control on SourceDataLine. Are you following the section “Getting the Controls from the Line” at http://download.oracle.com/javase/tutorial/sound/controls.html?
[/quote]
Yes, I’ve gone over that section a number of times. My Windows XP system don’t offer MASTER_VOLUME or VOLUME for a newly generated SourceDataLine. What OS are you using? When you change volume, do you get clicks? I do, on the MASTER_VOLUME made available for Clips, unless I keep the volume change increment very small, which limits how fast one can go from one volume to another.

I’m using the method described in “Getting a Line Directly from the AudioSystem,” from “Accessing Audio System Resources” to get my SourceDataLine. An exhaustive search for all Controls for all Mixers in my system only uncovered one control, for a Reverb of all things! So I assume it won’t help to try and get a SourceDataLine from a specified Mixer.

Even so, a control will only allow the “granularity” corresponding to the size of the byte buffer you use, yes? By putting a volume updater where you can iterate through the byte buffer itself, you get granularity per frame! 44100 increments per second! Undoubtably there are efficiencies to add to the following. This is first pass, just-get-it-working code and I’m happy to get suggestions for improvements.The byte[] dual is one stereo frame in size for my 16-bit wav files.

public class VolumeProcessor {

	public byte[] handleStaticVolume(byte[] buffer, 
			int numBytes, float volume)
	{
		float audioVal = 0.0f;
		for (int i = 0; i < numBytes; i += 2)
		{
			audioVal = (float) ( ( buffer[i+1] << 8 )
					| ( buffer[i] & 0xff )) * volume;
			buffer[i] = (byte)audioVal;
			buffer[i + 1] =	(byte)((int)audioVal >> 8 );
		}
		return buffer;
	}
	
}

int numBytesRead = 0;
					while (numBytesRead !=-1)
					{
						numBytesRead = aiStream.read(
								myData, 0, numBytesToRead);
						if (numBytesRead == -1) break;
//						myData = vp.handleStaticVolume(
//								myData, numBytesRead, volume);
						for (int i = 0; i < numBytesRead; i += 4)
						{
							if (volume <= volumeGoal)
							{
								volume += 0.00001f;
							}
							else
							{
								volume -= 0.00001f;
							}
							dual[0] = myData[i];
							dual[1] = myData[i+1];
							dual[2] = myData[i+2];
							dual[3] = myData[i+3];
							dual = vp.handleStaticVolume(
									dual, 4, volume);
							myData[i] = dual[0];
							myData[i+1] = dual[1];
							myData[i+2] = dual[2];
							myData[i+3] = dual[3];
						}
						
						
						line.write(myData, 0, numBytesRead);
					}

It is possible to get the FloatControl.MASTER_GAIN on SourceDataLine:


FloatControl volume = (FloatControl)line.getControl(FloatControl.MASTER_GAIN);
volume.setValue(<int>);

Look at the Java API next time :wink:

That is exactly the code I used! And I assume that works for most people’s setups. But it doesn’t work for mine, as stated previously.

Remind me what system you are running? I’m assuming you have implemented this and it works for you. I can get it to work for Clips but not SourceDataLines.

I’m wondering if it is an XP issue, or perhaps related to my odd audio setup. I run my sound through my Tascam mixer, via a MiaMIDI sound card. I don’t have a direct line out from the computer itself for sound except via this card, e.g., nothing can be heard from my computer’s headphone out. So maybe that is part of it. I knew I was going to use my studio monitors exclusively, so having internal sound seemed like something I could skip when I put the system together. Might have been overzealously frugal on that matter.

Not every OS/AudioSystem exposes every possible feature. A limited PC setup may not have some features. I suppose I could show a data dump of my AudioSystem if you don’t believe me.

But even if it did work, I don’t think I would use the MASTER_VOLUME control anymore, given the superior performance of working directly on the audio data! 8)

[s]Uhhh are you forgetting that you are using Java? Write once, run anywhere?

You are probably using a really old version of Java. What version are you using?[/s]

Misunderstood your problem, for a while I thought you said it wouldn’t compile or wouldn’t run.

So calling these methods don’t work? It must be either driver issues or hardware issues.