Browse Source

added progressive split buffer decoding for amortized cost

ncannasse 7 years ago
parent
commit
55ee8b2bca
1 changed files with 51 additions and 6 deletions
  1. 51 6
      hxd/snd/Manager.hx

+ 51 - 6
hxd/snd/Manager.hx

@@ -16,6 +16,11 @@ class Source {
 	public var playing = false;
 	public var start   = 0;
 
+	public var streamSound : hxd.res.Sound;
+	public var streamBuffer : haxe.io.Bytes;
+	public var streamStart : Int;
+	public var streamPos : Int;
+
 	public function new(driver : Driver) {
 		id      = ID++;
 		handle  = driver.createSource();
@@ -59,6 +64,11 @@ class Manager {
 	public static var MAX_SOURCES                = 16;
 	public static var SOUND_BUFFER_CACHE_SIZE    = 256;
 
+	/**
+		Allows to decode big streaming buffers over X split frames. 0 to disable
+	**/
+	public static var BUFFER_STREAM_SPLIT        = 16;
+
 	static var instance : Manager;
 
 	public var masterVolume	: Float;
@@ -377,7 +387,7 @@ class Manager {
 			c.source = s;
 
 			checkTargetFormat(c.sound.getData(), c.soundGroup.mono);
-			s.start = Math.ceil(c.position * targetRate);
+			s.start = Math.floor(c.position * targetRate);
 			if( s.start < 0 ) s.start = 0;
 			queueBuffer(s, c.sound, s.start);
 			c.positionChanged = false;
@@ -485,6 +495,30 @@ class Manager {
 	// internals
 	// ------------------------------------------------------------------------
 
+	function progressiveDecodeBuffer( s : Source, snd : hxd.res.Sound, start : Int ) {
+		var data = snd.getData();
+		var samples = Math.ceil(STREAM_BUFFER_SAMPLE_COUNT / BUFFER_STREAM_SPLIT);
+		if( s.streamStart != start || s.streamSound != snd ) {
+			s.streamSound = snd;
+			s.streamStart = start;
+			s.streamPos = start;
+		}
+		var end = start + STREAM_BUFFER_SAMPLE_COUNT;
+		if( s.streamPos == end )
+			return true; // already done
+		var bpp = data.getBytesPerSample();
+		var reqSize = STREAM_BUFFER_SAMPLE_COUNT * bpp;
+		if( s.streamBuffer == null || s.streamBuffer.length < reqSize ) {
+			s.streamBuffer = hxd.impl.Tmp.getBytes(reqSize);
+			s.streamPos = start;
+		}
+		var remain = end - s.streamPos;
+		if( remain > samples ) remain = samples;
+		data.decode(s.streamBuffer, (s.streamPos - start) * bpp, s.streamPos, remain);
+		s.streamPos += remain;
+		return s.streamPos == end;
+	}
+
 	function queueBuffer(s : Source, snd : hxd.res.Sound, start : Int) {
 		var data   = snd.getData();
 		var sgroup = s.channel.soundGroup;
@@ -495,12 +529,16 @@ class Manager {
 			b = getSoundBuffer(snd, sgroup);
 			driver.queueBuffer(s.handle, b.handle, start, true);
 		} else {
+
+			// wait until fully decoded
+			if( s.buffers.length > 0 && BUFFER_STREAM_SPLIT > 1 && !progressiveDecodeBuffer(s, snd, start) )
+				return;
+
 			// queue stream buffer
-			b = getStreamBuffer(snd, sgroup, start);
+			b = getStreamBuffer(s, snd, sgroup, start);
 			driver.queueBuffer(s.handle, b.handle, 0, b.isEnd);
 		}
 		s.buffers.push(b);
-		return b;
 	}
 
 	function unqueueBuffer(s : Source) {
@@ -607,7 +645,7 @@ class Manager {
 		buf.samples    = dat.samples;
 	}
 
-	function getStreamBuffer(snd : hxd.res.Sound, grp : SoundGroup, start : Int) : Buffer {
+	function getStreamBuffer( src : Source, snd : hxd.res.Sound, grp : SoundGroup, start : Int) : Buffer {
 		var data = snd.getData();
 
 		var b = freeStreamBuffers.shift();
@@ -629,8 +667,15 @@ class Manager {
 		b.start   = start;
 
 		var size  = samples * data.getBytesPerSample();
-		var bytes = getTmpBytes(size);
-		data.decode(bytes, 0, start, samples);
+		var bytes;
+		if( src.streamSound == snd && src.streamStart == start ) {
+			// finish progressive decoding and use progressive buffer
+			while( !progressiveDecodeBuffer(src, snd, start) ) {};
+			bytes = src.streamBuffer;
+		} else {
+			bytes = getTmpBytes(size);
+			data.decode(bytes, 0, start, samples);
+		}
 
 		if (!checkTargetFormat(data, grp.mono)) {
 			size = samples * targetChannels * Data.formatBytes(targetFormat);