Synchronizing events with a streaming audio soundtrack?

Post revision v.2: (hopefully more fun to read)

I want to play a streaming ogg file, and fire an event at second 8.043 of the song.

Things I have available: Jogl and Joal both running, but I can use anything at this point: I’m desperate! What audio systems will give me the capability to get playback position?

If you have the ogg streaming in place, just count the (decoded) samples you send to the soundcard to get the playback position.

Yes, I’m looking for code that does that :stuck_out_tongue:

how many samples per second for ogg? isn’t it variable bitrate?

edit: What I’ve got so far:

     new Thread(new Runnable(){
		public void run(){					
			try {
				Thread.sleep(500);
			} catch (Exception e){ }
			while(true){					
				try {
					Thread.sleep(1); //Every ms update
				} catch (Exception e){ }
				int[] values = new int[1];
				al.alGetSourcei(sourceID, AL.AL_BYTE_OFFSET, values,0);
				Position_MS = (int)(10000.0*(OggStreamer.this.totalBytes+values[0])/ OggStreamer.this.numBytesPerSample/ OggStreamer.this.numChannels / rate / 10.0);
				if (Position_MS>4926)
					System.out.println(Position_MS);
			}
		}
	}).start();

Now, this is very strange: AL_AL_BYTE_OFFSET remains unchanged every 20 iterations!!! Why is it only updated so infrequently!? This could kill my game

Does ANYONE have some better ideas?

[quote]how many samples per second for ogg? isn’t it variable bitrate?
[/quote]
That’s why I said decoded samples. If you stream an ogg file, it is decoded on the fly, and when the ogg file is 44.1KHz for example, you’re decoding 44100 samples a second (or 44100 * 2 for stereo).
Take a look at jorbis at http://www.jcraft.com/jorbis/, download the sourceand try to find in the code where the ogg file is decoded and where it sends the decoded samples to JavaSound. At that point you can add your counter (maybe jorbis even already has functionality for that, I’m not sure).

k, modified my above post in between you posting :stuck_out_tongue:

but ya, I figured out the samplerate for my file by looking at sample programs, it is currently working, but the resolution is very low: al_bytes_offset appears to be updated very infrequently

The resolution being low could be due to buffering, or maybe the offset is only updated per block in the ogg decoder (I’m not sure, I can’t see what you’re doing exactly just by looking at the code you posted).
If you want sub ms resolution, I’d really count the individual decoded samples in the ogg decoder, and add a getter somewhere to calculate the offset in MS from the samples offset.

Well, the problem is that I think that I need to use the BYTE_OFFSET, because that actuall says how many bytes have been played by the mixer (!!!) , but, for somereason, the resolution appears to be EXACTLY 10 ms. Soooo, I made a sub loop, that watches for its change.

So, we store the value of BYTE_OFFSET in a temp, and if it doesnt change between iteration, we increment a millisecond. If it changes, we’re at the new time.

cool :smiley:

edit: nahhhh actually that doesn’t work either :frowning: looks like I’ll have to deal with my 10 ms resolution :frowning:

THE RESULT OF THIS THREAD: This method gives you 10 ms resolution to ACTUAL MIXER PLAYBACK TIME. That is GOOD ENOUGH. (I’m making a music game, and it is spot on. Seriously, I stress tested it: It looks right on the beat.)

Code to synchronize audio through JOAL, because you can’t get this capability with javasound (because theres an unpredictable delay from javasound to mixer playback.

new Thread(new Runnable(){
			public void run(){
				while(true){
					try {
						Thread.sleep(1);
					} catch (Exception e){ };
					int[] values = new int[1];
					al.alGetSourcei(sourceID, AL.AL_BYTE_OFFSET, values,0);
					long temp_Position_MS = (int)(1000.0*(totalBytes-BUFFER_SIZE*2+values[0])/ numBytesPerSample/ numChannels / rate);
					if (Position_MS == temp_Position_MS) //THIS IS OPTIONAL, ITS JUST SO THAT YOU NEVER "HIT THE SAME TIME" TWICE, because BYTE_OFFSET has 10 ms resolution, slower than our iteration.
						Position_MS++;
					else
						Position_MS = temp_Position_MS;
				}
			}
		}).start();

Somewhere deep in the stream code:


byte[] pcm = new byte[BUFFER_SIZE];
		int size = 0;

		try {
			if ((size = oggDecoder.read(pcm)) <= 0)
				return false;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

		totalBytes += size;

The tester code:

double gap = .096;
				double bpm = 198.757763975155279;
				for (int a = 0; a < 400; a++){ //This if a very bad way to do it :p I'll fix it soon.
					if (audio.offOf((int)(1000*(gap+a*(60/bpm))))<20){
						Trenderer.draw("BAM!", (int)myCharacter.getX(), (int)myCharacter.getY());
					}
				}

and

	public long offOf(int target){
		long returnee = Position_MS - target;
		return returnee < 0?-returnee:returnee;
	}

[quote]because you can’t get this capability with javasound (because theres an unpredictable delay from javasound to mixer playback.
[/quote]
I know what you mean, it is caused by javasounds internal buffering and the sound driver’s buffering that lead to methods for measuring returning values with low coarse precision (I’ve been fighting with that issue too). It can still be done perfectly with JavaSound though, you just have to measure the time before the audio data enters this buffering and take the latency into account (defined by the size of the buffers), and not rely on measurement values from JavaSound.

But I’m glad you got it working fine in OpenAL, and yes, 10ms precision is probably just fine for a music game.