Nicolas Cannasse пре 8 година
родитељ
комит
5da4af1b00
3 измењених фајлова са 151 додато и 65 уклоњено
  1. 73 25
      hxd/snd/ALEmulator.hx
  2. 2 0
      hxd/snd/Channel.hx
  3. 76 40
      hxd/snd/Driver.hx

+ 73 - 25
hxd/snd/ALEmulator.hx

@@ -22,13 +22,32 @@ private class ALChannel extends NativeChannel {
 	override function onSample( out : haxe.io.Float32Array ) {
 		var pos = 0;
 		var count = out.length >> 1;
-		var volume = source.volume;
-		while( source.buffer != null && count > 0 ) {
-			var scount = source.buffer.samples - source.currentSample;
-			if( scount > 0 ) {
+		if( source.duration > 0 ) {
+			var volume = source.volume;
+			var bufferIndex = 0;
+			var baseSample = 0;
+			var curSample = source.currentSample;
+			var buffer = source.buffers[bufferIndex++];
+			while( count > 0 ) {
+				while( buffer != null && curSample >= buffer.samples ) {
+					baseSample += buffer.samples;
+					curSample -= buffer.samples;
+					buffer = source.buffers[bufferIndex++];
+				}
+				if( buffer == null ) {
+					if( source.loop ) {
+						curSample = 0;
+						baseSample = 0;
+						bufferIndex = 0;
+						buffer = source.buffers[bufferIndex++];
+						continue;
+					}
+					break;
+				}
+				var scount = buffer.samples - curSample;
 				if( scount > count ) scount = count;
-				var read = source.currentSample << 1;
-				var data = source.buffer.data;
+				var read = curSample << 1;
+				var data = buffer.data;
 				if( startup < 1 ) {
 					for( i in 0...scount ) {
 						out[pos++] = data[read++] * volume * startup;
@@ -45,13 +64,11 @@ private class ALChannel extends NativeChannel {
 					}
 				}
 				count -= scount;
-				source.currentSample += scount;
-			} else if( source.loop ) {
-				source.currentSample = 0;
-			} else {
-				break;
+				curSample += scount;
 			}
+			source.currentSample = baseSample + curSample;
 		}
+
 		for( i in 0...count<<1 )
 			out[pos++] = 0.;
 	}
@@ -72,16 +89,23 @@ class ALSource {
 
 	public var playedTime = 0.;
 	public var currentSample : Int = 0;
-	public var buffer : Buffer;
+	public var buffers : Array<Buffer> = [];
 	public var loop = false;
 	public var volume : F32 = 1.;
 	public var playing(get, never) : Bool;
+	public var duration : Float;
 
 	public function new() {
 		id = ++ID;
 		all.set(id, this);
 	}
 
+	public function updateDuration() {
+		duration = 0.;
+		for( b in buffers )
+			duration += b.samples / b.frequency;
+	}
+
 	inline function get_playing() return chan != null;
 
 	public function play() {
@@ -252,7 +276,7 @@ class ALEmulator {
 	public static function sourcef(source : Source, param : Int, value  : F32) {
 		switch( param ) {
 		case SEC_OFFSET:
-			source.currentSample = Std.int(value * source.buffer.frequency);
+			source.currentSample = source.buffers.length == 0 ? 0 : Std.int(value * source.buffers[0].frequency);
 		case GAIN:
 			source.volume = value;
 		default:
@@ -274,9 +298,10 @@ class ALEmulator {
 	public static function sourcei(source : Source, param : Int, value  : Int) {
 		switch( param ) {
 		case BUFFER:
-			source.buffer = Buffer.ofInt(value);
+			var b = Buffer.ofInt(value);
+			source.buffers = b == null ? [] : [b];
+			source.updateDuration();
 			source.currentSample = 0;
-			source.playedTime = haxe.Timer.stamp();
 		case LOOPING:
 			source.loop = value != 0;
 		default:
@@ -300,11 +325,11 @@ class ALEmulator {
 	public static function getSourcef(source : Source, param : Int) : F32 {
 		switch( param ) {
 		case SEC_OFFSET:
-			if( source.buffer == null )
+			if( source.buffers.length == 0 )
 				return 0;
 			var now = haxe.Timer.stamp();
 			var t = now - source.playedTime;
-			var maxT = source.buffer.samples / source.buffer.frequency;
+			var maxT = source.duration;
 			if( source.loop ) {
 				while( t > maxT ) {
 					t -= maxT;
@@ -320,7 +345,19 @@ class ALEmulator {
 	public static function getSourcei(source : Source, param : Int) : Int {
 		switch( param ) {
 		case SOURCE_STATE:
-			return !source.playing || source.buffer == null || (!source.loop && (haxe.Timer.stamp() - source.playedTime) >= source.buffer.samples/source.buffer.frequency ) ? STOPPED : PLAYING;
+			return !source.playing || source.buffers.length == 0 || (!source.loop && (haxe.Timer.stamp() - source.playedTime) >= source.duration ) ? STOPPED : PLAYING;
+		case BUFFERS_PROCESSED:
+			if( source.loop )
+				return 0;
+			var count = 0;
+			var cur = source.currentSample;
+			for( b in source.buffers )
+				if( cur >= b.samples ) {
+					cur -= b.samples;
+					count++;
+				} else
+					break;
+			return count;
 		default:
 			throw "Unsupported param 0x" + StringTools.hex(param);
 		}
@@ -353,6 +390,7 @@ class ALEmulator {
 	}
 
 	public static function sourcePlay(source : Source) {
+		source.playedTime = haxe.Timer.stamp();
 		source.play();
 	}
 
@@ -369,10 +407,25 @@ class ALEmulator {
 
 	// Queue buffers onto a source
 	public static function sourceQueueBuffers(source : Source, nb : Int, buffers : Bytes) {
-		throw "TODO";
+		var queue = [];
+		for( i in 0...nb ) {
+			var b = Buffer.ofInt(buffers.getInt32(i * 4));
+			queue.push(b);
+		}
+		source.buffers = queue;
+		source.updateDuration();
 	}
+
 	public static function sourceUnqueueBuffers(source : Source, nb : Int, buffers : Bytes) {
-		throw "TODO";
+		for( i in 0...nb ) {
+			var b = Buffer.ofInt(buffers.getInt32(i * 4));
+			if( b == source.buffers[0] ) {
+				source.buffers.shift();
+				source.currentSample -= b.samples;
+				source.playedTime += b.samples / b.frequency;
+				source.updateDuration();
+			}
+		}
 	}
 
 	// Buffer management
@@ -441,11 +494,6 @@ class ALEmulator {
 		}
 		buffer.samples = buffer.data.length >> 1;
 		buffer.frequency = freq;
-		// prevent infinite loop
-		if( buffer.samples == 0 ) {
-			buffer.samples = 128;
-			buffer.data = new haxe.ds.Vector(buffer.samples * 2);
-		}
 	}
 
 	// Set Buffer parameters

+ 2 - 0
hxd/snd/Channel.hx

@@ -63,6 +63,8 @@ class Channel extends ChannelBase {
 	**/
 	public function queueSound( sound : hxd.res.Sound ) {
 		queue.push(sound);
+		if( driver != null && source != null )
+			@:privateAccess driver.syncBuffers(source, this);
 	}
 
 	public function stop() {

+ 76 - 40
hxd/snd/Driver.hx

@@ -19,7 +19,7 @@ private typedef ALContext = hxd.snd.ALEmulator.ALContext;
 class Source {
 	public var inst : ALSource;
 	public var channel : Channel;
-	public var buffer : Buffer;
+	public var buffers : Array<Buffer>;
 
 	public var loop = false;
 	public var volume = 1.;
@@ -27,9 +27,11 @@ class Source {
 
 	public var nextSound : hxd.res.Sound;
 	public var nextBuffer : Buffer;
+	public var hasQueue = false;
 
 	public function new(inst) {
 		this.inst = inst;
+		buffers = [];
 	}
 }
 
@@ -68,7 +70,7 @@ class Driver {
 
 	static inline var AL_NUM_SOURCES = 16;
 
-	var tmpBytes : haxe.io.Bytes;
+	var cachedBytes : haxe.io.Bytes;
 
 	var alDevice      : ALDevice;
 	var alContext     : ALContext;
@@ -98,7 +100,13 @@ class Driver {
 		AL.genSources(AL_NUM_SOURCES, bytes);
 		sources = [for (i in 0...AL_NUM_SOURCES) new Source(ALSource.ofInt(bytes.getInt32(i * 4)))];
 
-		tmpBytes = haxe.io.Bytes.alloc(4 * 3 * 2);
+		cachedBytes = haxe.io.Bytes.alloc(4 * 3 * 2);
+	}
+
+	function getTmp(size) {
+		if( cachedBytes.length < size )
+			cachedBytes = haxe.io.Bytes.alloc(size);
+		return cachedBytes;
 	}
 
 	static function soundUpdate() {
@@ -175,7 +183,18 @@ class Driver {
 					c.position = position;
 					c.lastStamp = now;
 					c.positionChanged = false;
-					if( position < prev ) c.onEnd();
+					if( c.queue.length > 0 ) {
+						var count = AL.getSourcei(s.inst, AL.BUFFERS_PROCESSED);
+						while( count > 0 ) {
+							var tmp = getTmp(4);
+							tmp.setInt32(0, s.buffers[0].inst.toInt());
+							AL.sourceUnqueueBuffers(s.inst, 1, tmp);
+							queueNext(c);
+							count--;
+							c.onEnd();
+						}
+					} else if( position < prev )
+						c.onEnd();
 				}
 			default:
 			}
@@ -225,6 +244,7 @@ class Driver {
 		AL.listener3f(AL.POSITION, listener.position.x, listener.position.y, listener.position.z);
 
 		listener.direction.normalize();
+		var tmpBytes = getTmp(24);
 		tmpBytes.setFloat(0,  listener.direction.x);
 		tmpBytes.setFloat(4,  listener.direction.y);
 		tmpBytes.setFloat(8,  listener.direction.z);
@@ -256,7 +276,7 @@ class Driver {
 			c.source = s;
 
 			// bind buf and force full sync
-			setBuffer(s, getBuffer(c.sound, c.soundGroup));
+			syncBuffers(s, c);
 			c.positionChanged = true;
 			c = c.next;
 		}
@@ -291,9 +311,10 @@ class Driver {
 			AL.sourcef(s.inst, AL.SEC_OFFSET, c.position);
 			c.positionChanged = false;
 		}
-		if( s.loop != c.loop ) {
-			s.loop = c.loop;
-			AL.sourcei(s.inst, AL.LOOPING, c.loop ? AL.TRUE : AL.FALSE);
+		var loopFlag = c.loop && c.queue.length == 0;
+		if( s.loop != loopFlag ) {
+			s.loop = loopFlag;
+			AL.sourcei(s.inst, AL.LOOPING, loopFlag ? AL.TRUE : AL.FALSE);
 		}
 		var v = c.volume * c.channelGroup.volume * c.soundGroup.volume;
 		if( s.volume != v ) {
@@ -302,31 +323,11 @@ class Driver {
 		}
 		for(e in c.effects)
 			e.apply(c, s);
+
 		if( !s.playing ) {
 			s.playing = true;
 			AL.sourcePlay(s.inst);
 		}
-
-		// sync queuing
-		var nextSound = c.queue[0];
-		if( s.nextSound != nextSound ) {
-			if( s.nextSound != null ) {
-				tmpBytes.setInt32(0, s.nextBuffer.inst.toInt());
-				AL.sourceUnqueueBuffers(s.inst, 1, tmpBytes);
-				s.nextBuffer.unref();
-				s.nextSound = null;
-				s.nextBuffer = null;
-			}
-			if( nextSound != null ) {
-				s.nextSound = nextSound;
-				s.nextBuffer = getBuffer(nextSound, c.soundGroup);
-				s.nextBuffer.playCount++;
-				tmpBytes.setInt32(0, s.nextBuffer.inst.toInt());
-				AL.sourceQueueBuffers(s.inst, 1, tmpBytes);
-				if( AL.getError() != 0 )
-					throw "Failed to queue buffer : format differs between " + c.sound + " and " + nextSound;
-			}
-		}
 	}
 
 	function queueNext( c : Channel ) {
@@ -350,20 +351,53 @@ class Driver {
 			s.playing = false;
 			AL.sourceStop(s.inst);
 		}
-		setBuffer(s, null);
+		syncBuffers(s, null);
 	}
 
-	function setBuffer( s : Source, b : Buffer ) {
-		if( s.buffer == b )
-			return;
-		if( b == null ) {
-			AL.sourcei(s.inst, AL.BUFFER, AL.NONE);
-			s.buffer.unref();
-			s.buffer = null;
+	function syncBuffers( s : Source, c : Channel ) {
+		if( c == null ) {
+			if( s.buffers.length == 0 )
+				return;
+			if( !s.hasQueue )
+				AL.sourcei(s.inst, AL.BUFFER, AL.NONE);
+			else {
+				var tmpBytes = getTmp(4 * s.buffers.length);
+				for( i in 0...s.buffers.length )
+					tmpBytes.setInt32(i << 2, s.buffers[i].inst.toInt());
+				AL.sourceUnqueueBuffers(s.inst, s.buffers.length, tmpBytes);
+			}
+			for( b in s.buffers )
+				b.unref();
+			s.buffers = [];
+			s.hasQueue = false;
+		} else if( s.hasQueue || c.queue.length > 0 ) {
+
+			if( !s.hasQueue && s.buffers.length > 0 )
+				throw "Can't queue on a channel that is currently playing an unstreamed data";
+
+			var buffers = [getBuffer(c.sound, c.soundGroup)];
+			for( snd in c.queue )
+				buffers.push(getBuffer(snd, c.soundGroup));
+			var tmpBytes = getTmp(buffers.length * 4);
+			for( i in 0...buffers.length ) {
+				var b = buffers[i];
+				b.playCount++;
+				tmpBytes.setInt32(i << 2, b.inst.toInt());
+			}
+			for( b in s.buffers )
+				b.unref();
+			AL.sourceQueueBuffers(s.inst, buffers.length, tmpBytes);
+			s.buffers = buffers;
+			if( AL.getError() != 0 )
+				throw "Failed to queue buffers : format differs";
+
 		} else {
-			AL.sourcei(s.inst, AL.BUFFER, b.inst.toInt());
-			b.playCount++;
-			s.buffer = b;
+			var buffer = getBuffer(c.sound, c.soundGroup);
+			AL.sourcei(s.inst, AL.BUFFER, buffer.inst.toInt());
+			if( s.buffers[0] != null )
+				s.buffers[0].unref();
+			s.buffers[0] = buffer;
+			buffer.playCount++;
 		}
 	}
 
@@ -378,6 +412,7 @@ class Driver {
 				if( b.playCount == 0 && b.lastStop < now - 60 )
 					releaseBuffer(b);
 		}
+		var tmpBytes = getTmp(4);
 		AL.genBuffers(1, tmpBytes);
 		var b = new Buffer(ALBuffer.ofInt(tmpBytes.getInt32(0)));
 		b.sound = snd;
@@ -393,6 +428,7 @@ class Driver {
 		buffers.remove(b);
 		bufferMap.remove(b.sound);
 		@:privateAccess b.sound.data = null; // free cached decoded data
+		var tmpBytes = getTmp(4);
 		tmpBytes.setInt32(0, b.inst.toInt());
 		AL.deleteBuffers(1, tmpBytes);
 	}