Parcourir la source

refactoring on new sound system

Nicolas Cannasse il y a 8 ans
Parent
commit
ad106035a8

+ 30 - 3
hxd/res/Sound.hx

@@ -1,11 +1,36 @@
 package hxd.res;
 
+enum SoundFormat {
+	Wav;
+	Mp3;
+	OggVorbis;
+}
+
 class Sound extends Resource {
 
 	var data : hxd.snd.Data;
 	var channel : hxd.snd.Channel;
 	public var lastPlay(default, null) = 0.;
 
+	public static function supportedFormat( fmt : SoundFormat ) {
+		return switch( fmt ) {
+		case Wav:
+			return true;
+		case Mp3:
+			#if (flash || js)
+			return true;
+			#else
+			return false;
+			#end
+		case OggVorbis:
+			#if stb_ogg_sound
+			return true;
+			#else
+			return false;
+			#end
+		}
+	}
+
 	public function getData() : hxd.snd.Data {
 		if( data != null )
 			return data;
@@ -48,9 +73,11 @@ class Sound extends Resource {
 
 	#if hl
 
-	public function play( ?loop = false, volume = 1. ) {
-		throw "TODO";
-		return null;
+	public function play( ?loop = false, ?channelGroup, ?soundGroup ) {
+		lastPlay = haxe.Timer.stamp();
+		channel = hxd.snd.Driver.get().play(this, channelGroup, soundGroup);
+		channel.loop = loop;
+		return channel;
 	}
 
 	#else

+ 117 - 6
hxd/snd/Data.hx

@@ -1,16 +1,127 @@
 package hxd.snd;
 
+enum SampleFormat {
+	I8;
+	I16;
+	F32;
+}
+
 class Data {
 
 	public var samples(default, null) : Int;
+	public var samplingRate(default, null) : Int;
+	public var sampleFormat(default, null) : SampleFormat;
+	public var channels(default, null) : Int;
+
 	public var duration(get, never) : Float;
 
-	/**
-		Decode sound samples as stereo 32 bit floats 44.1Khz
-		If you decode while no data is available, blank samples will be written.
-	**/
 	public function decode( out : haxe.io.Bytes, outPos : Int, sampleStart : Int, sampleCount : Int ) : Void {
-		throw "not implemented";
+		var bpp = getBytesPerSample();
+		if( sampleStart < 0 || sampleCount < 0 || outPos < 0 || outPos + sampleCount * bpp > out.length )
+			throw haxe.io.Error.OutsideBounds;
+		if( sampleStart + sampleCount >= samples ) {
+			var count = 0;
+			if( sampleStart < samples ) {
+				count = samples - sampleStart;
+				decodeBuffer(out, outPos, sampleStart, count);
+			}
+			out.fill(outPos + count*bpp, (sampleCount - count) * bpp, 0);
+			return;
+		}
+		decodeBuffer(out, outPos, sampleStart, sampleCount);
+	}
+
+	public function resample( rate : Int, format : SampleFormat, channels : Int ) : Data {
+		if( sampleFormat == format && samplingRate == rate && this.channels == channels )
+			return this;
+
+		var newSamples = Math.ceil(samples * (rate / samplingRate));
+		var bpp = getBytesPerSample();
+		var data = haxe.io.Bytes.alloc(bpp * samples);
+		var resample = samples != newSamples;
+		decodeBuffer(data, 0, 0, samples);
+
+		var out = new haxe.io.BytesBuffer();
+		var srcChannels = this.channels;
+		var commonChannels = channels < srcChannels ? channels : srcChannels;
+		var extraChannels = (channels > srcChannels ? channels : srcChannels) - commonChannels;
+		var sval = 0., ival = 0;
+		for( i in 0...newSamples ) {
+			var targetSample = (i / (newSamples - 1)) * (samples - 1);
+			var isample = Std.int(targetSample);
+			var offset = targetSample - isample;
+			var srcPos = isample * bpp;
+			for( k in 0...commonChannels ) {
+				var sval1, sval2 = 0.;
+
+				inline function sext8(v:Int) {
+					return (v & 0x80) == 0 ? v : v | 0xFFFFFF00;
+				}
+				inline function sext16(v:Int) {
+					return (v & 0x8000) == 0 ? v : v | 0xFFFF0000;
+				}
+
+				switch( sampleFormat ) {
+				case I8:
+					sval1 = sext8(data.get(srcPos)) / 128;
+					if( resample ) sval2 = sext8(data.get(srcPos + bpp)) / 128;
+					srcPos++;
+				case I16:
+					sval1 = sext16(data.getUInt16(srcPos)) / 0x8000;
+					if( resample ) sval2 = sext16(data.getUInt16(srcPos + bpp)) / 0x8000;
+					srcPos += 2;
+				case F32:
+					sval1 = data.getFloat(srcPos);
+					if( resample ) sval2 = data.getFloat(srcPos + bpp);
+					srcPos += 4;
+				}
+
+				sval = resample ? hxd.Math.lerp(sval1, sval2, offset) : sval1;
+				switch( format ) {
+				case I8:
+					ival = Std.int(sval * 128);
+					if( ival > 127 ) ival = 127;
+					out.addByte(ival & 0xFF);
+				case I16:
+					ival = Std.int(sval * 0x8000);
+					if( ival > 0x7FFF ) ival = 0x7FFF;
+					out.addByte(ival & 0xFF);
+					out.addByte((ival>>>8) & 0xFF);
+				case F32:
+					out.addFloat(sval);
+				}
+			}
+			for( i in 0...extraChannels )
+				switch( format ) {
+				case I8:
+					out.addByte(ival & 0xFF);
+				case I16:
+					out.addByte(ival & 0xFF);
+					out.addByte((ival>>>8) & 0xFF);
+				case F32:
+					out.addFloat(sval);
+				}
+		}
+
+		var data = new WavData(null);
+		data.channels = channels;
+		data.samples = newSamples;
+		data.sampleFormat = format;
+		data.samplingRate = rate;
+		@:privateAccess data.rawData = out.getBytes();
+		return data;
+	}
+
+	function decodeBuffer( out : haxe.io.Bytes, outPos : Int, sampleStart : Int, sampleCount : Int ) : Void {
+		throw "Not implemented";
+	}
+
+	public function getBytesPerSample() {
+		return channels * switch( sampleFormat ) {
+		case I8: 1;
+		case I16: 2;
+		case F32: 4;
+		}
 	}
 
 	/**
@@ -23,7 +134,7 @@ class Data {
 	}
 
 	function get_duration() {
-		return samples / 44100;
+		return samples / samplingRate;
 	}
 
 }

+ 1 - 1
hxd/snd/LoadingData.hx

@@ -9,7 +9,7 @@ class LoadingData extends Data {
 		this.snd = snd;
 	}
 
-	override public function decode(out:haxe.io.Bytes, outPos:Int, sampleStart:Int, sampleCount:Int):Void {
+	override function decode(out:haxe.io.Bytes, outPos:Int, sampleStart:Int, sampleCount:Int):Void {
 		var d = snd.getData();
 		if( Std.is(d, LoadingData) )
 			throw "Sound data is not yet available, use load() first";

+ 23 - 12
hxd/snd/Mp3Data.hx

@@ -28,16 +28,20 @@ class Mp3Data extends Data {
 			samples -= start + end + 1152; // first frame is empty
 		}
 
-		var sampling = format.mp3.Constants.MPEG.srEnum2Num(mp.frames[0].header.samplingRate);
-		#if flash
-		if( sampling != 44100 )
-			samples = Math.ceil(samples * 44100.0 / sampling);
-		#elseif js
-		var ctx = @:privateAccess NativeChannel.getContext();
-		samples = Math.ceil(samples * ctx.sampleRate / sampling);
-		#end
+		var header = mp.frames[0].header;
+		sampleFormat = F32;
+		samplingRate = format.mp3.Constants.MPEG.srEnum2Num(header.samplingRate);
+		channels = header.channelMode == Mono ? 1 : 2;
 
 		#if flash
+
+		// flash only allows to decode mp3 in stereo 44.1Khz
+		channels = 2;
+		if( samplingRate != 44100 ) {
+			samples = Math.ceil(samples * 44100.0 / samplingRate);
+			samplingRate = 44100;
+		}
+
 		snd = new flash.media.Sound();
 		bytes.getData().position = 0;
 		snd.loadCompressedDataFromByteArray(bytes.getData(), bytes.length);
@@ -54,6 +58,13 @@ class Mp3Data extends Data {
 
 	function processBuffer( buf : js.html.audio.AudioBuffer ) {
 		var left = buf.getChannelData(0);
+		samples = buf.length; // actual decoded samples
+
+		if( channels == 1 ) {
+			buffer = haxe.io.Bytes.ofData(left);
+			return;
+		}
+
 		var right = buf.numberOfChannels < 2 ? left : buf.getChannelData(1);
 		var join = new js.html.Float32Array(left.length * 2);
 		var w = 0;
@@ -61,7 +72,7 @@ class Mp3Data extends Data {
 			join[w++] = left[i];
 			join[w++] = right[i];
 		}
-		samples = buf.length; // actual decoded samples
+
  		buffer = haxe.io.Bytes.ofData(join.buffer);
 		if( onEnd != null ) {
 			onEnd();
@@ -70,7 +81,7 @@ class Mp3Data extends Data {
 	}
 	#end
 
-	override public function decode(out:haxe.io.Bytes, outPos:Int, sampleStart:Int, sampleCount:Int) {
+	override function decodeBuffer(out:haxe.io.Bytes, outPos:Int, sampleStart:Int, sampleCount:Int) {
 		#if flash
 		var b = out.getData();
 		b.position = outPos;
@@ -90,9 +101,9 @@ class Mp3Data extends Data {
 		#elseif js
 		if( buffer == null ) {
 			// not yet available : fill with blanks
-			out.fill(outPos, sampleCount * 8, 0);
+			out.fill(outPos, sampleCount * 4 * channels, 0);
 		} else {
-			out.blit(outPos, buffer, sampleStart * 8, sampleCount * 8);
+			out.blit(outPos, buffer, sampleStart * 4 * channels, sampleCount * 4 * channels);
 		}
 		#else
 		throw "MP3 decoding is not available for this platform";

+ 40 - 9
hxd/snd/OggData.hx

@@ -34,6 +34,16 @@ private class BytesOutput extends haxe.io.Output {
 		position += 4;
 	}
 
+	override function writeInt16(i) {
+		#if flash
+		m.wb(position++, i >> 8);
+		m.wb(position++, i);
+		#else
+		bytes.setUInt16(position, i);
+		position += 2;
+		#end
+	}
+
 }
 
 class OggData extends Data {
@@ -46,34 +56,55 @@ class OggData extends Data {
 	static inline var CACHED_SAMPLES = 44100 * 3; // 3s of cached sound
 
 	public function new( bytes : haxe.io.Bytes ) {
-		reader = stb.format.vorbis.Reader.openFromBytes(bytes);
-		samples = reader.totalSample;
+		if( bytes != null ) {
+			reader = stb.format.vorbis.Reader.openFromBytes(bytes);
+			samples = reader.totalSample;
+			channels = reader.header.channel;
+			samplingRate = reader.header.sampleRate;
+			sampleFormat = F32;
+		}
 		output = new BytesOutput();
 		decodedFirst = 0;
 		decodedLast = 0;
 	}
 
-	override public function decode(out:haxe.io.Bytes, outPos:Int, sampleStart:Int, sampleCount:Int) {
+	override function resample(rate, format, channels):Data {
+		if( sampleFormat == format && samplingRate == rate && this.channels == channels )
+			return this;
+		switch( format ) {
+		case I16, F32 if( rate % this.samplingRate == 0 && channels >= this.channels ):
+			var c = new OggData(null);
+			c.reader = reader;
+			c.samples = samples;
+			c.samplingRate = samplingRate;
+			c.sampleFormat = format;
+			c.channels = channels;
+			return c;
+		default:
+			return super.resample(rate, format, channels);
+		}
+	}
+
+	override function decodeBuffer(out:haxe.io.Bytes, outPos:Int, sampleStart:Int, sampleCount:Int) {
 		var last = sampleStart + sampleCount;
-		if( last > samples )
-			throw "OUTSIDE";
+		var bpp = getBytesPerSample();
 		if( sampleStart < decodedFirst || last > decodedLast ) {
 			var need = sampleCount - sampleStart;
 			if( need > CACHED_SAMPLES || samples > CACHED_SAMPLES ) {
 				// directly decode in out
 				output.init(out, outPos);
 				reader.currentSample = sampleStart;
-				reader.read(output, sampleCount, 2, 44100, true);
+				reader.read(output, sampleCount, channels, samplingRate, sampleFormat == F32);
 				output.done();
 				return;
 			}
 			if( decoded == null )
-				decoded = haxe.io.Bytes.alloc((samples < CACHED_SAMPLES ? samples : CACHED_SAMPLES) * 2 * 4);
+				decoded = haxe.io.Bytes.alloc((samples < CACHED_SAMPLES ? samples : CACHED_SAMPLES) * bpp);
 			need = CACHED_SAMPLES;
 			if( sampleStart + need > samples ) need = samples - sampleStart;
 			output.init(decoded,0);
 			reader.currentSample = sampleStart;
-			reader.read(output, need, 2, 44100, true);
+			reader.read(output, need, channels, samplingRate, sampleFormat == F32);
 			output.done();
 			decodedFirst = sampleStart;
 			decodedLast = sampleStart + need;
@@ -82,7 +113,7 @@ class OggData extends Data {
 				output = null;
 			}
 		}
-		out.blit(outPos, decoded, (sampleStart - decodedFirst) * 2 * 4, sampleCount * 2 * 4);
+		out.blit(outPos, decoded, (sampleStart - decodedFirst) * bpp, sampleCount * bpp);
 	}
 
 

+ 13 - 75
hxd/snd/WavData.hx

@@ -6,89 +6,27 @@ class WavData extends hxd.snd.Data {
 	var rawData : haxe.io.Bytes;
 
 	public function new(bytes) {
-		init(new format.wav.Reader(new haxe.io.BytesInput(bytes)).read());
+		if( bytes != null )
+			init(new format.wav.Reader(new haxe.io.BytesInput(bytes)).read());
 	}
 
 	function init(d:format.wav.Data.WAVE) {
 		var h = d.header;
-		if( h.channels > 2 || (h.bitsPerSample != 8 && h.bitsPerSample != 16) )
-			throw "Unsupported WAV " + h.bitsPerSample + " bits " + h.channels + " chans";
-
-		var data = d.data;
-		if( h.samplingRate != 44100 ) {
-			// resample, without increasing bit resolution
-			var out = new haxe.io.BytesOutput();
-			var rpos = 0.;
-			var max = data.length >> (h.bitsPerSample >> 4);
-			var delta = h.samplingRate / 44100;
-			var next = h.channels;
-			while( rpos < max ) {
-				var ipos = Std.int(rpos);
-				var npos = ipos + next;
-				if( npos >= max ) npos = max - next;
-				var v1, v2;
-
-				inline function getUI8(p) {
-					return data.get(p);
-				}
-				inline function getI16(p) {
-					var v = data.get(p) | (data.get(p + 1) << 8);
-					if( v & 0x8000 != 0 ) v -= 0x10000;
-					return v;
-				}
-				if( h.bitsPerSample == 8 ) {
-					v1 = getUI8(ipos);
-					v2 = getUI8(npos);
-				} else {
-					v1 = getI16(ipos<<1);
-					v2 = getI16(npos<<1);
-				}
-				var v = Std.int(hxd.Math.lerp(v1, v2, rpos - ipos));
-				if( h.bitsPerSample == 8 )
-					out.writeByte(v);
-				else
-					out.writeInt16(v);
-				rpos += delta;
-			}
-			data = out.getBytes();
-		}
-
-		var out = new haxe.io.BytesOutput();
-		var input = new haxe.io.BytesInput(data);
-		switch( [h.channels, h.bitsPerSample] ) {
-		case [2, 16]:
-			samples = data.length >> 2;
-			for( i in 0...samples ) {
-				out.writeFloat(input.readInt16() / 32767);
-				out.writeFloat(input.readInt16() / 32767);
-			}
-		case [1, 16]:
-			samples = data.length >> 1;
-			for( i in 0...samples ) {
-				var f = input.readInt16() / 32767;
-				out.writeFloat(f);
-				out.writeFloat(f);
-			}
-		case [1, 8]:
-			samples = data.length;
-			for( i in 0...samples ) {
-				var f = input.readByte() / 255;
-				out.writeFloat(f);
-				out.writeFloat(f);
-			}
-		case [2, 8]:
-			samples = data.length >> 1;
-			for( i in 0...samples ) {
-				out.writeFloat(input.readByte() / 255);
-				out.writeFloat(input.readByte() / 255);
-			}
+		samplingRate = h.samplingRate;
+		channels = h.channels;
+		sampleFormat = switch( h.bitsPerSample ) {
+		case 8: I8;
+		case 16: I16;
 		default:
+			throw "Unsupported WAV " + h.bitsPerSample + " bits";
 		}
-		rawData = out.getBytes();
+		rawData = d.data;
+		samples = Std.int(rawData.length / getBytesPerSample());
 	}
 
-	override public function decode(out:haxe.io.Bytes, outPos:Int, sampleStart:Int, sampleCount:Int) {
-		out.blit(outPos, rawData, sampleStart * 8, sampleCount * 8);
+	override function decodeBuffer(out:haxe.io.Bytes, outPos:Int, sampleStart:Int, sampleCount:Int) {
+		var bpp = getBytesPerSample();
+		out.blit(outPos, rawData, sampleStart * bpp, sampleCount * bpp);
 	}
 
 }

+ 15 - 9
hxd/snd2/hxd/snd/Channel.hx

@@ -1,15 +1,14 @@
 package hxd.snd;
 
-@:allow(hxd.snd.System)
 class Channel extends ChannelBase {
 	static var ID = 0;
 
 	@:noCompletion public var next     : Channel;
-	@:noCompletion public var nextFree : Channel;
+	var driver : Driver;
+	var source : Driver.Source;
 
 	public var id           (default, null) : Int;
-	public var soundRes     (default, null) : hxd.res.Sound;
-	public var soundData    (default, null) : hxd.snd.Data;
+	public var sound     	(default, null) : hxd.res.Sound;
 	public var soundGroup   (default, null) : SoundGroup;
 	public var channelGroup (default, null) : ChannelGroup;
 	public var duration     (default, null) : Float;
@@ -29,6 +28,9 @@ class Channel extends ChannelBase {
 		id = ++ID;
 	}
 
+	public dynamic function onEnd() {
+	}
+
 	function set_position(v : Float) {
 		lastStamp = haxe.Timer.stamp();
 		positionChanged = true;
@@ -40,16 +42,16 @@ class Channel extends ChannelBase {
 		return pause = v;
 	}
 
-	function init(sound : hxd.res.Sound) {
+	function init(driver, sound : hxd.res.Sound) {
 		reset();
+		this.driver = driver;
 		pause     = false;
 		isVirtual = false;
 		loop      = false;
 		lastStamp = haxe.Timer.stamp();
 
-		soundRes  = sound;
-		soundData = sound.getData();
-		duration  = soundData.samples / 44100;
+		this.sound  = sound;
+		duration  = sound.getData().duration;
 		position  = 0.0;
 		audibleGain = 1.0;
 		initStamp = haxe.Timer.stamp();
@@ -62,6 +64,10 @@ class Channel extends ChannelBase {
 	}
 
 	public function stop() {
-		@:privateAccess System.instance.releaseChannel(this);
+		if( driver != null ) {
+			@:privateAccess driver.releaseChannel(this);
+			driver = null;
+		}
 	}
+
 }

+ 3 - 2
hxd/snd2/hxd/snd/ChannelBase.hx

@@ -1,13 +1,14 @@
 package hxd.snd;
 
 class ChannelBase {
+
 	public var priority : Float;
 	public var volume   : Float;
 	public var pan      : Float;
 	public var mute     : Bool;
 	public var effects  : Array<Effect>;
 
-	public function new() {
+	function new() {
 		reset();
 	}
 
@@ -30,7 +31,7 @@ class ChannelBase {
 	}
 
 	public function reset() {
-		effects  = [];
+		if( effects == null || effects.length > 0 ) effects  = [];
 		priority = 0.0;
 		volume   = 1.0;
 		pan      = 0.0;

+ 4 - 2
hxd/snd2/hxd/snd/ChannelGroup.hx

@@ -1,10 +1,12 @@
 package hxd.snd;
 
-@:allow(hxd.snd.System)
 class ChannelGroup extends ChannelBase {
+
 	public var name (default, null) : String;
-	private function new(name : String) {
+
+	public function new(name : String) {
 		super();
 		this.name = name;
 	}
+
 }

+ 359 - 0
hxd/snd2/hxd/snd/Driver.hx

@@ -0,0 +1,359 @@
+package hxd.snd;
+
+import openal.AL;
+import openal.ALC;
+
+class Source {
+	public var inst : openal.AL.Source;
+	public var channel : Channel;
+	public var buffer : Buffer;
+
+	public function new(inst) {
+		this.inst = inst;
+	}
+}
+
+class Buffer {
+	public var inst : openal.AL.Buffer;
+	public var sound : hxd.res.Sound;
+	public var playCount : Int;
+	public var lastStop : Float;
+
+	public function new(inst) {
+		this.inst = inst;
+	}
+}
+
+@:access(hxd.snd.Channel)
+@:access(hxd.snd.Effect)
+class Driver {
+	static var instance : Driver;
+
+	public var masterVolume	: Float;
+	public var masterSoundGroup   (default, null) : SoundGroup;
+	public var masterChannelGroup (default, null) : ChannelGroup;
+
+	public var listener : Listener;
+
+	var channels : Channel;
+
+	// ------------------------------------------------------------------------
+	// AL SHIT
+	// ------------------------------------------------------------------------
+
+	static inline var AL_NUM_SOURCES = 16;
+
+	var tmpBytes : haxe.io.Bytes;
+
+	var alDevice      : Device;
+	var alContext     : Context;
+
+	var buffers       : Array<Buffer>;
+	var sources       : Array<Source>;
+	var bufferMap     : Map<hxd.res.Sound, Buffer>;
+
+	// ------------------------------------------------------------------------
+
+	private function new() {
+		masterVolume       = 1.0;
+		masterSoundGroup   = new SoundGroup  ("master");
+		masterChannelGroup = new ChannelGroup("master");
+		listener = new Listener();
+
+		buffers = [];
+		bufferMap = new Map();
+
+		// al init
+		alDevice  = ALC.openDevice(null);
+		alContext = ALC.createContext(alDevice, null);
+		ALC.makeContextCurrent(alContext);
+
+		// alloc sources
+		var bytes = haxe.io.Bytes.alloc(4 * AL_NUM_SOURCES);
+		AL.genSources(AL_NUM_SOURCES, bytes);
+		sources = [for (i in 0...AL_NUM_SOURCES) new Source(cast bytes.getInt32(i * 4))];
+
+		tmpBytes = haxe.io.Bytes.alloc(4 * 3 * 2);
+	}
+
+	static function soundUpdate() {
+		if( instance != null ) instance.update();
+	}
+
+	public static function get() {
+		if( instance == null ) {
+			instance = new Driver();
+			haxe.MainLoop.add(soundUpdate);
+		}
+		return instance;
+	}
+
+	public function stopAll() {
+		while( channels != null )
+			channels.stop();
+	}
+
+	public function dispose() {
+		stopAll();
+
+		AL.deleteSources(sources.length, hl.Bytes.getArray([for( s in sources ) s.inst]));
+		AL.deleteBuffers(buffers.length, hl.Bytes.getArray([for( b in buffers ) b.inst]));
+		sources = [];
+		buffers = [];
+
+		ALC.makeContextCurrent(null);
+		ALC.destroyContext(alContext);
+		ALC.closeDevice(alDevice);
+	}
+
+	public function play(sound : hxd.res.Sound, ?soundGroup : SoundGroup, ?channelGroup : ChannelGroup) {
+		if (soundGroup   == null) soundGroup   = masterSoundGroup;
+		if (channelGroup == null) channelGroup = masterChannelGroup;
+		var c = new Channel();
+		c.init(this, sound);
+		c.soundGroup   = soundGroup;
+		c.channelGroup = channelGroup;
+		c.next = channels;
+		channels = c;
+		return c;
+	}
+
+	public function update() {
+		// update playing channels from sources & release stopped channels
+		for( s in sources ) {
+			var c = s.channel;
+			if( c == null ) continue;
+			var state  = 0;
+			AL.getSourcei(s.inst, AL.SOURCE_STATE, new hl.Ref(state));
+			switch (state) {
+			case AL.STOPPED:
+				releaseChannel(c);
+				c.onEnd();
+			case AL.PLAYING:
+				if (!c.positionChanged) {
+					var position : hl.F32 = 0.0;
+					AL.getSourcef(s.inst, AL.SEC_OFFSET, new hl.Ref(position));
+					c.position = position;
+					c.positionChanged = false;
+				}
+			default:
+			}
+		}
+
+		// calc audible gain & virtualize inaudible channels
+		var c = channels;
+		while (c != null) {
+			c.isVirtual = false;
+			c.calcAudibleGain();
+			if (c.pause || c.mute || c.channelGroup.mute || c.audibleGain < 1e-5 ) c.isVirtual = true;
+			c = c.next;
+		}
+
+		// sort channels by priority
+		channels = haxe.ds.ListSort.sortSingleLinked(channels, sortChannel);
+
+		{	// virtualize sounds that puts the put the audible count over the maximum number of sources
+			var sgroupRefs = new Map<SoundGroup, Int>();
+
+			var audibleCount = 0;
+			var c = channels;
+			while (c != null && !c.isVirtual) {
+				if (++audibleCount > sources.length) c.isVirtual = true;
+				else if (c.soundGroup.maxAudible >= 0) {
+					var sgRefs = sgroupRefs.get(c.soundGroup);
+					if (sgRefs == null) sgRefs = 0;
+					if (++sgRefs > c.soundGroup.maxAudible) {
+						c.isVirtual = true;
+						--audibleCount;
+					}
+					sgroupRefs.set(c.soundGroup, sgRefs);
+				}
+				c = c.next;
+			}
+		}
+
+
+		// free sources that points to virtualized channels
+		for ( s in sources ) {
+			if ( s.channel == null || !s.channel.isVirtual) continue;
+			releaseSource(s);
+		}
+
+		// update listener parameters
+		AL.listenerf(AL.GAIN, masterVolume);
+		AL.listener3f(AL.POSITION, listener.position.x, listener.position.y, listener.position.z);
+
+		listener.direction.normalize();
+		tmpBytes.setFloat(0,  listener.direction.x);
+		tmpBytes.setFloat(4,  listener.direction.y);
+		tmpBytes.setFloat(8,  listener.direction.z);
+
+		listener.up.normalize();
+		tmpBytes.setFloat(12, listener.up.x);
+		tmpBytes.setFloat(16, listener.up.y);
+		tmpBytes.setFloat(20, listener.up.z);
+
+		AL.listenerfv(AL.ORIENTATION, tmpBytes);
+
+		// bind sources to non virtual channels
+		var c = channels;
+		while (c != null) {
+			if( c.source != null || c.isVirtual ) {
+				c = c.next;
+				continue;
+			}
+
+			// look for a free source
+			var s = null;
+			for( s2 in sources )
+				if( s2.channel == null ) {
+					s = s2;
+					break;
+				}
+			if( s == null ) throw "assert";
+			s.channel = c;
+			c.source = s;
+
+			// bind buf & play source
+			setBuffer(s, getBuffer(c));
+			AL.sourcePlay(s.inst);
+			AL.sourcef(s.inst, AL.SEC_OFFSET, c.position);
+			c.positionChanged = false;
+			c = c.next;
+		}
+
+		// update source paramaters
+		for ( s in sources ) {
+			var c = s.channel;
+			if( c == null) continue;
+
+			if (c.positionChanged) {
+				AL.sourcef(s.inst, AL.SEC_OFFSET, c.position);
+				c.positionChanged = false;
+			}
+
+			AL.sourcei(s.inst, AL.LOOPING, c.loop ? AL.TRUE : AL.FALSE);
+			AL.sourcef(s.inst, AL.GAIN, c.volume * c.channelGroup.volume * c.soundGroup.volume);
+
+			for(e in c.effects)
+				e.apply(c, s);
+		}
+
+		// update virtual channels
+		var c = channels;
+		var now = haxe.Timer.stamp();
+		while (c != null) {
+			var next = c.next;
+			if (!c.pause && c.isVirtual) {
+				c.position += now - c.lastStamp;
+				if (!c.loop && c.position >= c.duration)
+					releaseChannel(c);
+			}
+			c = next;
+		}
+	}
+
+	// ------------------------------------------------------------------------
+	// internals
+	// ------------------------------------------------------------------------
+
+	function releaseSource( s : Source ) {
+		s.channel = null;
+		AL.sourceStop(s.inst);
+		setBuffer(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.playCount--;
+			if( s.buffer.playCount == 0 ) s.buffer.lastStop = haxe.Timer.stamp();
+			s.buffer = null;
+		} else {
+			AL.sourcei(s.inst, AL.BUFFER, b.inst);
+			b.playCount++;
+			s.buffer = b;
+		}
+	}
+
+	function getBuffer( c : Channel ) : Buffer {
+		var b = bufferMap.get(c.sound);
+		if( b != null )
+			return b;
+		if( buffers.length >= 256 ) {
+			// cleanup unused buffers
+			var now = haxe.Timer.stamp();
+			for( b in buffers.copy() )
+				if( b.playCount == 0 && b.lastStop < now - 60 )
+					releaseBuffer(b);
+		}
+		AL.genBuffers(1, tmpBytes);
+		var b = new Buffer(cast tmpBytes.getInt32(0));
+		b.sound = c.sound;
+		buffers.push(b);
+		bufferMap.set(c.sound, b);
+		fillBuffer(b, c.sound.getData(), c.soundGroup.mono);
+		return b;
+	}
+
+	function releaseBuffer( b : Buffer ) {
+		buffers.remove(b);
+		bufferMap.remove(b.sound);
+		@:privateAccess b.sound.data = null; // free cached decoded data
+		tmpBytes.setInt32(0, cast b.inst);
+		AL.deleteBuffers(1, tmpBytes);
+	}
+
+	function sortChannel(a : Channel, b : Channel) {
+		if (a.isVirtual != b.isVirtual)
+			return (a.isVirtual && !b.isVirtual) ? 1 : -1;
+
+		if (a.channelGroup.priority != b.channelGroup.priority)
+			return a.channelGroup.priority < b.channelGroup.priority ? 1 : -1;
+
+		if (a.priority != b.priority)
+			return a.priority < b.priority ? 1 : -1;
+
+		if (a.audibleGain != b.audibleGain)
+			return a.audibleGain < b.audibleGain ? 1 : -1;
+
+		return a.initStamp < b.initStamp ? 1 : -1;
+	}
+
+	function releaseChannel(c : Channel) {
+		if (channels == c) {
+			channels = c.next;
+		} else {
+			var prev = channels;
+			while (prev.next != c)
+				prev = prev.next;
+			prev.next = c.next;
+		}
+		c.next = null;
+		if( c.source != null ) {
+			releaseSource(c.source);
+			c.source = null;
+		}
+	}
+
+	function fillBuffer(buf : Buffer, dat : hxd.snd.Data, forceMono = false) {
+		var targetRate = dat.samplingRate;
+		var targetChannels = forceMono || dat.channels == 1 ? 1 : 2;
+		var alFormat;
+		var targetFormat : hxd.snd.Data.SampleFormat = switch( dat.sampleFormat ) {
+		case I8:
+			alFormat = targetChannels == 1 ? AL.FORMAT_MONO8 : AL.FORMAT_STEREO8;
+			I8;
+		case I16, F32:
+			alFormat = targetChannels == 1 ? AL.FORMAT_MONO16 : AL.FORMAT_STEREO16;
+			I16;
+		}
+		if( targetChannels != dat.channels || targetFormat != dat.sampleFormat || targetRate != dat.samplingRate )
+			dat = dat.resample(targetRate, targetFormat, targetChannels);
+		var dataBytes = haxe.io.Bytes.alloc(dat.samples * dat.getBytesPerSample());
+		dat.decode(dataBytes, 0, 0, dat.samples);
+		AL.bufferData(buf.inst, alFormat, dataBytes, dataBytes.length, dat.samplingRate);
+	}
+}

+ 2 - 5
hxd/snd2/hxd/snd/Effect.hx

@@ -1,6 +1,5 @@
 package hxd.snd;
 
-@:allow(audiov2.System)
 class Effect {
 	public var gain (get, set) : Float;
 
@@ -10,11 +9,9 @@ class Effect {
 		return v;
 	}
 
-	private function new() { }
+	function new() { }
 
-	#if hl
-	function apply(channel : Channel, source : openal.AL.Source) {
+	function apply( channel : Channel, source : Driver.Source ) {
 	}
-	#end
 
 }

+ 24 - 0
hxd/snd2/hxd/snd/Listener.hx

@@ -0,0 +1,24 @@
+package hxd.snd;
+
+class Listener {
+
+	public var position : h3d.Vector;
+	public var direction : h3d.Vector;
+	public var velocity : h3d.Vector;
+	public var up  : h3d.Vector;
+
+	public function new() {
+		position = new h3d.Vector();
+		velocity = new h3d.Vector();
+		direction = new h3d.Vector(1,  0, 0);
+		up = new h3d.Vector(0,  0,  1);
+	}
+
+	public function syncCamera( cam : h3d.Camera ) {
+		position.load(cam.pos);
+		direction.set(cam.target.x - cam.pos.x, cam.target.y - cam.pos.y, cam.target.z - cam.pos.z);
+		direction.normalize();
+		up.load(cam.up);
+	}
+
+}

+ 1 - 2
hxd/snd2/hxd/snd/SoundGroup.hx

@@ -1,6 +1,5 @@
 package hxd.snd;
 
-@:allow(hxd.snd.System)
 class SoundGroup {
 	public var name (default, null) : String;
 	public var volume               : Float;
@@ -9,7 +8,7 @@ class SoundGroup {
 	public var muteFadeSpeed        : Float;
 	public var mono					: Bool;
 
-	private function new(name : String) {
+	public function new(name : String) {
 		this.name  = name;
 		maxAudible = -1;
 		muteFadeSpeed = 0;

+ 0 - 403
hxd/snd2/hxd/snd/System.hx

@@ -1,403 +0,0 @@
-package hxd.snd;
-
-import openal.AL;
-import openal.ALC;
-
-class System {
-	public static var instance : System;
-
-	public var masterVolume	: Float;
-	public var masterSoundGroup   (default, null) : SoundGroup;
-	public var masterChannelGroup (default, null) : ChannelGroup;
-
-	var soundGroups   : Array<SoundGroup>;
-	var channelGroups : Array<ChannelGroup>;
-	var effects       : Array<Effect>;
-
-	public var listenerPos : h3d.Vector;
-	public var listenerVel : h3d.Vector;
-	public var listenerDir : h3d.Vector;
-	public var listenerUp  : h3d.Vector;
-
-	var usedChannels : Channel;
-	var freeChannels : Channel;
-
-	// ------------------------------------------------------------------------
-	// AL SHIT
-	// ------------------------------------------------------------------------
-
-	var AL_NUM_SOURCES = 16;
-	var AL_NUM_BUFFERS = 256;
-
-	var alListenerOriBytes : haxe.io.Bytes;
-
-	var alDevice      : Device;
-	var alContext     : Context;
-
-	var alBuffers     : Array<Buffer>;
-	var alBufRefs     : Array<Int>;
-	var alFreeBufs    : Array<Int>;
-	var soundToBuf    : Map<hxd.res.Sound, Int>;
-	var bufToSound    : Map<Int, hxd.res.Sound>;
-
-	var alSources     : Array<Source>;
-	var chanToSource  : Map<Channel, Int>;
-	var sourceToChan  : Array<Channel>;
-	var alFreeSources : Array<Int>;
-
-	// ------------------------------------------------------------------------
-
-	private function new() {
-		masterVolume       = 1.0;
-		masterSoundGroup   = new SoundGroup  ("master");
-		masterChannelGroup = new ChannelGroup("master");
-
-		soundGroups   = [masterSoundGroup];
-		channelGroups = [masterChannelGroup];
-		effects       = [];
-
-		listenerPos = new h3d.Vector();
-		listenerVel = new h3d.Vector();
-		listenerDir = new h3d.Vector(0,  0, -1);
-		listenerUp  = new h3d.Vector(0,  1,  0);
-
-		{
-			// al init
-			alDevice  = ALC.openDevice(null);
-			alContext = ALC.createContext(alDevice, null);
-			ALC.makeContextCurrent(alContext);
-
-			var bytes = haxe.io.Bytes.alloc(4 * AL_NUM_SOURCES);
-			AL.genSources(AL_NUM_SOURCES, bytes);
-			alSources = [];
-			for (i in 0...AL_NUM_SOURCES) alSources.push(cast bytes.getInt32(i * 4));
-
-			var bytes = haxe.io.Bytes.alloc(4 * AL_NUM_BUFFERS);
-			AL.genBuffers(AL_NUM_BUFFERS, bytes);
-			alBuffers = [];
-			for (i in 0...AL_NUM_BUFFERS) alBuffers.push(cast bytes.getInt32(i * 4));
-		}
-
-		alBufRefs  = [];
-		alFreeBufs = [];
-
-		for (i in 0...alBuffers.length) {
-			alBufRefs.push(0);
-			alFreeBufs.push(i);
-		}
-
-		sourceToChan  = [];
-		alFreeSources = [];
-		for (i in 0...alSources.length) {
-			alFreeSources.push(i);
-			sourceToChan.push(null);
-		}
-
-		soundToBuf = new Map();
-		bufToSound = new Map();
-		chanToSource = new Map();
-		alListenerOriBytes = haxe.io.Bytes.alloc(4 * 3 * 2);
-	}
-
-	public static function init() {
-		instance = new System();
-	}
-
-	public function shutdown() {
-		for (s in alSources) {
-			var buf : Int = AL.NONE;
-			AL.getSourcei(s, AL.BUFFER, new hl.Ref(buf));
-			if (buf != AL.NONE) AL.sourcei(s, AL.BUFFER, AL.NONE);
-		}
-
-		AL.deleteSources(AL_NUM_SOURCES, hl.Bytes.getArray(alSources));
-		AL.deleteBuffers(AL_NUM_BUFFERS, hl.Bytes.getArray(alBuffers));
-
-		ALC.makeContextCurrent(null);
-		ALC.destroyContext(alContext);
-		ALC.closeDevice(alDevice);
-	}
-
-	public function createSoundGroup(name : String) {
-		var sg = new SoundGroup(name);
-		soundGroups.push(sg);
-		return sg;
-	}
-
-	public function createChannelGroup(name : String) {
-		var cg = new ChannelGroup(name);
-		channelGroups.push(cg);
-		return cg;
-	}
-
-	public function releaseSoundGroup(sg : SoundGroup) {
-		soundGroups.remove(sg);
-	}
-
-	public function releaseChannelGroup(cg : ChannelGroup) {
-		channelGroups.remove(cg);
-	}
-
-	public function createEffect<T:Effect>(etype : Class<T>, ?args : Array<Dynamic>) : T {
-		if (args == null) args = [];
-		var e = Type.createInstance(etype, args);
-		return e;
-	}
-
-	public function releaseEffect(e : Effect) {
-		effects.remove(e);
-	}
-
-	public function playSound(sound : hxd.res.Sound, ?soundGroup : SoundGroup, ?channelGroup : ChannelGroup) {
-		if (soundGroup   == null) soundGroup   = masterSoundGroup;
-		if (channelGroup == null) channelGroup = masterChannelGroup;
-		var c = acquireChannel(sound, soundGroup, channelGroup);
-
-		var index = soundToBuf.get(sound);
-		if (index == null) {
-			index = alFreeBufs.pop();
-			if (index == null) throw "too many buffers";
-			soundToBuf.set(sound, index);
-			fillBuffer(alBuffers[index], c.soundData, soundGroup.mono);
-		} else alFreeBufs.remove(index);
-
-		var oldSound = bufToSound.get(index);
-		if (oldSound != null && oldSound != sound) {
-			soundToBuf.remove(oldSound);
-			alBufRefs[index] = 0;
-		}
-
-		bufToSound.set(index, sound);
-		alBufRefs[index] = alBufRefs[index] + 1;
-
-		return c;
-	}
-
-	public function update() {
-		// update playing channels from sources & release stopped channels
-		for (i in 0...alSources.length) {
-			var c = sourceToChan[i];
-			if (c == null) continue;
-			var state  = 0;
-			var source = alSources[i];
-			AL.getSourcei(source, AL.SOURCE_STATE, new hl.Ref(state));
-			switch (state) {
-				case AL.STOPPED :
-					releaseChannel(c);
-				case AL.PLAYING :
-					if (!c.positionChanged) {
-						var position : hl.F32 = 0.0;
-						AL.getSourcef(source, AL.SEC_OFFSET, new hl.Ref(position));
-						c.position = position;
-						c.positionChanged = false;
-					}
-				default :
-			}
-		}
-
-		// calc audible gain & virtualize inaudible channels
-		var c = usedChannels;
-		while (c != null) {
-			c.isVirtual = false;
-			c.calcAudibleGain();
-			if (c.pause || c.mute || c.channelGroup.mute || c.audibleGain == 0) c.isVirtual = true;
-			c = c.next;
-		}
-
-		// sort channels by priority
-		usedChannels = haxe.ds.ListSort.sortSingleLinked(usedChannels, sortChannel);
-
-		{	// virtualize sounds that puts the put the audible count over the maximum number of sources
-			var sgroupRefs = new Map<SoundGroup, Int>();
-
-			var audibleCount = 0;
-			var c = usedChannels;
-			while (c != null && !c.isVirtual) {
-				if (++audibleCount > alSources.length) c.isVirtual = true;
-				else if (c.soundGroup.maxAudible >= 0) {
-					var sgRefs = sgroupRefs.get(c.soundGroup);
-					if (sgRefs == null) sgRefs = 0;
-					if (++sgRefs > c.soundGroup.maxAudible) {
-						c.isVirtual = true;
-						--audibleCount;
-					}
-					sgroupRefs.set(c.soundGroup, sgRefs);
-				}
-				c = c.next;
-			}
-		}
-
-
-		// free sources that points to virtualized channels
-		for (i in 0...alSources.length) {
-			if (sourceToChan[i] == null || !sourceToChan[i].isVirtual) continue;
-			releaseSource(i);
-		}
-
-		// bind sources to non virtual channels
-		var c = usedChannels;
-		while (c != null) {
-			var srcIndex = chanToSource.get(c);
-			if (srcIndex != null || c.isVirtual) {
-				c = c.next;
-				continue;
-			}
-
-			srcIndex = alFreeSources.pop();
-			if (srcIndex == null) throw "woups " + c.id;
-			chanToSource.set(c, srcIndex);
-			sourceToChan[srcIndex] = c;
-
-			// bind buf & play source
-			var source = alSources[srcIndex];
-			var buffer = alBuffers[soundToBuf.get(c.soundRes)];
-			AL.sourcei(source, AL.BUFFER, buffer);
-			AL.sourcePlay(source);
-			AL.sourcef(source, AL.SEC_OFFSET, c.position);
-			c.positionChanged = false;
-			c = c.next;
-		}
-
-		{	// update listener parameters
-			AL.listenerf(AL.GAIN, masterVolume);
-			AL.listener3f(AL.POSITION, listenerPos.x, listenerPos.y, listenerPos.z);
-
-			alListenerOriBytes.setFloat(0,  listenerDir.x);
-			alListenerOriBytes.setFloat(4,  listenerDir.y);
-			alListenerOriBytes.setFloat(8,  listenerDir.z);
-
-			alListenerOriBytes.setFloat(12, listenerUp.x);
-			alListenerOriBytes.setFloat(16, listenerUp.y);
-			alListenerOriBytes.setFloat(20, listenerUp.z);
-
-			AL.listenerfv(AL.ORIENTATION, alListenerOriBytes);
-		}
-
-		// update source paramaters
-		for (i in 0...alSources.length) {
-			var c = sourceToChan[i];
-			var source = alSources[i];
-			if (c == null) continue;
-
-			if (c.positionChanged) {
-				AL.sourcef(source, AL.SEC_OFFSET, c.position);
-				c.positionChanged = false;
-			}
-
-			AL.sourcei(source, AL.LOOPING, c.loop ? AL.TRUE : AL.FALSE);
-			AL.sourcef(source, AL.GAIN, c.volume * c.channelGroup.volume * c.soundGroup.volume);
-
-			for (e in c.effects) @:privateAccess e.apply(c, source);
-		}
-
-		// update virtual channels
-		var c = usedChannels;
-		while (c != null) {
-			if (!c.pause && c.isVirtual) {
-				c.position += haxe.Timer.stamp() - c.lastStamp;
-				if (!c.loop && c.position >= c.duration)
-					releaseChannel(c);
-			}
-			c = c.next;
-		}
-	}
-
-	// ------------------------------------------------------------------------
-	// internals
-	// ------------------------------------------------------------------------
-
-	function releaseSource(i : Int) {
-		chanToSource.remove(sourceToChan[i]);
-		sourceToChan[i] = null;
-		alFreeSources.push(i);
-
-		var source = alSources[i];
-		AL.sourceStop(source);
-		AL.sourcei(source, AL.BUFFER, AL.NONE);
-	}
-
-	function sortChannel(a : Channel, b : Channel) {
-		if (a.isVirtual != b.isVirtual)
-			return (a.isVirtual && !b.isVirtual) ? 1 : -1;
-
-		if (a.channelGroup.priority != b.channelGroup.priority)
-			return a.channelGroup.priority < b.channelGroup.priority ? 1 : -1;
-
-		if (a.priority != b.priority)
-			return a.priority < b.priority ? 1 : -1;
-
-		if (a.audibleGain != b.audibleGain)
-			return a.audibleGain < b.audibleGain ? 1 : -1;
-
-		return a.initStamp < b.initStamp ? 1 : -1;
-	}
-
-	function acquireChannel(sound : hxd.res.Sound, soundGroup : SoundGroup, channelGroup : ChannelGroup) {
-		var c : Channel = null;
-
-		if (freeChannels == null) {
-			c = new Channel();
-		} else {
-			c = freeChannels;
-			freeChannels = c.nextFree;
-			c.nextFree   = null;
-		}
-
-		c.init(sound);
-
-		c.soundGroup   = soundGroup;
-		c.channelGroup = channelGroup;
-
-		c.next = usedChannels;
-		usedChannels = c;
-
-		return c;
-	}
-
-	function releaseChannel(c : Channel) {
-		if (usedChannels == c) {
-			usedChannels = c.next;
-		} else {
-			var prev = usedChannels;
-			while (prev.next != c)
-				prev = prev.next;
-			prev.next = c.next;
-		}
-
-		var bi = soundToBuf.get(c.soundRes);
-		alBufRefs[bi] = alBufRefs[bi] - 1;
-		if (alBufRefs[bi] == 0) alFreeBufs.unshift(bi);
-
-		c.next       = null;
-		c.nextFree   = freeChannels;
-		freeChannels = c;
-
-		var isrc = chanToSource.get(c);
-		if (isrc != null) releaseSource(isrc);
-	}
-
-	function fillBuffer(buf : Buffer, dat : hxd.snd.Data, ?mono = false) {
-		var nsamples  = dat.samples;
-		var nchannels = mono ? 1 : 2;
-
-		var floatBytes = haxe.io.Bytes.alloc(nsamples * 2 * 4);
-		var shortBytes = haxe.io.Bytes.alloc(nsamples * nchannels * 2);
-
-		dat.decode(floatBytes, 0, 0, nsamples);
-		if (nchannels == 2) for (i in 0...nsamples) {
-			shortBytes.setUInt16(i * 4 + 0, Std.int(floatBytes.getFloat(i * 8 + 0) * 32767));
-			shortBytes.setUInt16(i * 4 + 2, Std.int(floatBytes.getFloat(i * 8 + 4) * 32767));
-		} else for (i in 0...nsamples) {
-			var valL = Std.int(floatBytes.getFloat(i * 8 + 0) * 32767);
-			var valR = Std.int(floatBytes.getFloat(i * 8 + 4) * 32767);
-			shortBytes.setUInt16(i * 2, (valL + valR) >> 1);
-		}
-
-		var format = switch(nchannels) {
-			case 1 : AL.FORMAT_MONO16;
-			case 2 : AL.FORMAT_STEREO16;
-			default : throw "unsupported sound format";
-		}
-		AL.bufferData(buf, format, shortBytes, shortBytes.length, 44100);
-	}
-}

+ 15 - 14
hxd/snd2/hxd/snd/effect/Spatialization.hx

@@ -1,9 +1,9 @@
-package audiov2.effect;
+package hxd.snd.effect;
 
 import openal.AL;
 
-@:keep
 class Spatialization extends Effect {
+
 	public var position  : h3d.Vector;
 	public var velocity  : h3d.Vector;
 	public var direction : h3d.Vector;
@@ -13,7 +13,7 @@ class Spatialization extends Effect {
 	public var fadeDistance : Null<Float>;
 	public var rollOffFactor : Float;
 
-	private function new() {
+	public function new() {
 		super();
 		position  = new h3d.Vector();
 		velocity  = new h3d.Vector();
@@ -24,7 +24,7 @@ class Spatialization extends Effect {
 	}
 
 	function getFadeGain() {
-		var dist = audiov2.System.instance.listenerPos.distance(position);
+		var dist = System.get().listener.position.distance(position);
 		if (maxDistance != null) dist -= maxDistance;
 		else dist -= referenceDistance;
 		var gain = 1 - dist / fadeDistance;
@@ -33,32 +33,33 @@ class Spatialization extends Effect {
 		return gain;
 	}
 
-	override function apply(channel : Channel, source : Source) {
-		AL.source3f(source, AL.POSITION,  position.x,  position.y,  position.z);
-		AL.source3f(source, AL.VELOCITY,  velocity.x,  velocity.y,  velocity.z);
-		AL.source3f(source, AL.DIRECTION, direction.x, direction.y, direction.z);
+	override function apply(channel : Channel, s : System.Source) {
+		AL.source3f(s.inst, AL.POSITION,  position.x,  position.y,  position.z);
+		AL.source3f(s.inst, AL.VELOCITY,  velocity.x,  velocity.y,  velocity.z);
+		AL.source3f(s.inst, AL.DIRECTION, direction.x, direction.y, direction.z);
 
-		AL.sourcef(source, AL.REFERENCE_DISTANCE, referenceDistance);
-		AL.sourcef(source, AL.ROLLOFF_FACTOR, rollOffFactor);
-		AL.sourcef(source, AL.MIN_GAIN, 0);
+		AL.sourcef(s.inst, AL.REFERENCE_DISTANCE, referenceDistance);
+		AL.sourcef(s.inst, AL.ROLLOFF_FACTOR, rollOffFactor);
+		AL.sourcef(s.inst, AL.MIN_GAIN, 0);
 
 		if (maxDistance != null) {
 			var md : Float = maxDistance;
-			AL.sourcef(source, AL.MAX_DISTANCE, md);
+			AL.sourcef(s.inst, AL.MAX_DISTANCE, md);
 		}
 
 		if (fadeDistance != null) {
 			var volume = channel.volume * channel.soundGroup.volume * channel.channelGroup.volume;
-			AL.sourcef(source, AL.GAIN, getFadeGain() * volume);
+			AL.sourcef(s.inst, AL.GAIN, getFadeGain() * volume);
 		}
 	}
 
 	override function get_gain() {
-		var dist = audiov2.System.instance.listenerPos.distance(position);
+		var dist = System.get().listener.position.distance(position);
 		dist = Math.max(dist, referenceDistance);
 		if (maxDistance != null) dist = Math.min(dist, maxDistance);
 		var gain = referenceDistance/(referenceDistance + rollOffFactor * (dist - referenceDistance));
 		if (fadeDistance != null) gain *= getFadeGain();
 		return gain;
 	}
+
 }

+ 17 - 8
samples/Sound.hx

@@ -17,13 +17,19 @@ class NoiseChannel extends hxd.snd.NativeChannel {
 class Sound extends hxd.App {
 
 	var time = 0.;
-	static var music : hxd.snd.Worker;
 
 	override function init() {
-		var c = new NoiseChannel();
-		haxe.Timer.delay(c.stop, 1000);
-		var c = hxd.Res.music_loop.play(true);
-		c.onEnd = function() trace("LOOP");
+		var res = if( hxd.res.Sound.supportedFormat(Mp3) )
+			hxd.Res.music_loop_mp3
+		else if( hxd.res.Sound.supportedFormat(OggVorbis) )
+			hxd.Res.music_loop_ogg;
+		else
+			null;
+		if( res != null ) {
+			trace("Playing "+res);
+			var c = res.play(true);
+			c.onEnd = function() trace("LOOP");
+		}
 	}
 
 	override function update(dt:Float) {
@@ -34,12 +40,15 @@ class Sound extends hxd.App {
 			engine.backgroundColor = 0xFFFF0000;
 		} else
 			engine.backgroundColor = 0;
+
+		if( hxd.Key.isPressed(hxd.Key.SPACE) ) {
+			var c = new NoiseChannel();
+			haxe.Timer.delay(c.stop, 1000);
+		}
 	}
 
 	static function main() {
-		hxd.Res.initEmbed({compressSounds:true});
-		if( hxd.res.Sound.startWorker() )
-			return;
+		hxd.Res.initEmbed();
 		new Sound();
 	}
 

BIN
samples/sound_res/music_loop.ogg