diff --git a/source/lime/_internal/backend/html5/HTML5AudioSource.hx b/source/lime/_internal/backend/html5/HTML5AudioSource.hx index 7fd31c4e..5d66fc04 100644 --- a/source/lime/_internal/backend/html5/HTML5AudioSource.hx +++ b/source/lime/_internal/backend/html5/HTML5AudioSource.hx @@ -251,16 +251,6 @@ class HTML5AudioSource public function getPosition():Vector4 { - #if lime_howlerjs - // This should work, but it returns null (But checking the inside of the howl, the _pos is actually null... so ¯\_(ツ)_/¯) - /* - var arr = parent.buffer.__srcHowl.pos()) - position.x = arr[0]; - position.y = arr[1]; - position.z = arr[2]; - */ - #end - return position; } @@ -278,4 +268,16 @@ class HTML5AudioSource return position; } + + public function getPan():Float + { + return position.x; + } + + public function setPan(value:Float):Float + { + position.setTo(value, 0, -Math.sqrt(1 - value * value)); + if (parent.buffer != null && parent.buffer.__srcHowl != null && parent.buffer.__srcHowl.stereo != null) parent.buffer.__srcHowl.stereo(value, id); + return value; + } } diff --git a/source/lime/_internal/backend/native/NativeAudioSource.hx b/source/lime/_internal/backend/native/NativeAudioSource.hx index 9f0939d7..d7cbdb51 100644 --- a/source/lime/_internal/backend/native/NativeAudioSource.hx +++ b/source/lime/_internal/backend/native/NativeAudioSource.hx @@ -13,6 +13,7 @@ import lime.media.vorbis.Vorbis; import lime.media.vorbis.VorbisFile; #end +import lime.math.Vector2; import lime.math.Vector4; import lime.media.AudioBuffer; import lime.media.AudioSource; @@ -30,6 +31,11 @@ class NativeAudioSource { private static var STREAM_TIMER_FREQUENCY:Int = 100; private static var STREAM_BUFFER_FREQUENCY:Int = 3; + private static var moreFormatsSupported:Null; + private static var stereoAnglesExtensionSupported:Null; + private static var loopPointsSupported:Null; // TODO: implement loop Points for looped static sources + private static var latencyExtensionSupported:Null; + private var parent:AudioSource; private var disposed:Bool; private var streamed:Bool; @@ -54,7 +60,9 @@ class NativeAudioSource { private var streamEnded:Bool; private var dataLength:Float; - private var position:Vector4 = new Vector4(); + private var position:Vector4; + private var angles:Vector2; + private var anglesArray:Array; private var length:Null; private var loopTime:Null; private var loops:Int; @@ -65,6 +73,10 @@ class NativeAudioSource { disposed = true; stop(); + position = null; + angles = null; + anglesArray = null; + if (handle != null) { AL.sourcei(handle, AL.BUFFER, AL.NONE); AL.deleteSource(handle); @@ -81,16 +93,26 @@ class NativeAudioSource { public function init() { if (handle != null) return; - if (disposed = (handle = AL.createSource()) == null) return; + + if (position == null) position = new Vector4(); + if (angles == null) angles = new Vector2(Math.PI / 6, -Math.PI / 6); // https://github.com/kcat/openal-soft/issues/1032 + anglesArray = [0, 0]; + + if (moreFormatsSupported == null) moreFormatsSupported = AL.isExtensionPresent("AL_EXT_MCFORMATS"); + if (stereoAnglesExtensionSupported == null) stereoAnglesExtensionSupported = AL.isExtensionPresent("AL_EXT_STEREO_ANGLES"); + if (latencyExtensionSupported == null) latencyExtensionSupported = AL.isExtensionPresent("AL_SOFT_source_latency"); + if (loopPointsSupported == null) loopPointsSupported = AL.isExtensionPresent("AL_SOFT_loop_points"); + AL.sourcef(handle, AL.MAX_GAIN, 10); + AL.distanceModel(AL.NONE); var buffer = parent.buffer; var channels = buffer.channels, bitsPerSample = buffer.bitsPerSample; // Default is just AL.FORMAT_MONO8 if it doesn't match any to avoid yo ears getting blasted var isCreativeXFi = AL.getString(AL.RENDERER) == "X-Fi"; - if (channels > 2 && (isCreativeXFi || AL.isExtensionPresent("AL_EXT_MCFORMATS"))) { // https://github.com/openalext/openalext/blob/master/AL_EXT_MCFORMATS.txt + if (channels > 2 && (isCreativeXFi || moreFormatsSupported)) { // https://github.com/openalext/openalext/blob/master/AL_EXT_MCFORMATS.txt if (channels == 3) format = bitsPerSample == 32 ? 0x1209 : (bitsPerSample == 16 ? 0x1208 : 0x1207); else if (channels == 4) format = bitsPerSample == 32 ? 0x1206 : (bitsPerSample == 16 ? 0x1205 : 0x1204); else if (channels == 6) format = bitsPerSample == 32 ? 0x120C : (bitsPerSample == 16 ? 0x120B : 0x120A); @@ -466,7 +488,7 @@ class NativeAudioSource { public function getLatency():Float { /*#if (lime >= "8.3.0") - if (AL.isExtensionPresent("AL_SOFT_source_latency")) { + if (latencyExtensionSupported) { final offsets = AL.getSourcedvSOFT(handle, AL.SEC_OFFSET_LATENCY_SOFT, 2); if (offsets != null) return offsets[1] * 1000; } @@ -474,7 +496,28 @@ class NativeAudioSource { return 0; } - public function getPosition():Vector4 return position; + public function getAngles():Vector2 { + if (angles == null) angles = new Vector2(Math.PI / 6, -Math.PI / 6); + return angles; + } + + public function setAngles(left:Float, right:Float):Vector2 { + if (angles == null) angles = new Vector2(left, right); + else angles.setTo(left, right); + + if (!disposed) { + anglesArray[0] = angles.x; + anglesArray[1] = angles.y; + AL.sourcei(handle, 0x1214/*AL.SOURCE_SPATIALIZE_SOFT*/, AL.FALSE); + AL.sourcefv(handle, 0x1030/*AL.STEREO_ANGLES*/, anglesArray); + } + return angles; + } + + public function getPosition():Vector4 { + if (position == null) position = new Vector4(); + return position; + } public function setPosition(value:Vector4):Vector4 { position.x = value.x; @@ -482,13 +525,28 @@ class NativeAudioSource { position.z = value.z; position.w = value.w; + // OpenAL Soft Positions doesn't seem to do anything but panning? if (!disposed) { - AL.distanceModel(AL.NONE); + AL.sourcei(handle, 0x1214/*AL.SOURCE_SPATIALIZE_SOFT*/, Math.abs(position.x) > 1e-04 ? AL.TRUE : AL.FALSE); + AL.sourcei(handle, AL.MAX_DISTANCE, 1); AL.source3f(handle, AL.POSITION, position.x, position.y, position.z); } return position; } + public function getPan():Float return getPosition().x; + + public function setPan(value:Float):Float { + getPosition().setTo(value, 0, -Math.sqrt(1 - value * value)); + if (!disposed) { + if (parent.buffer.channels > 1 && stereoAnglesExtensionSupported) + setAngles(Math.PI * (Math.min(-value * 2 + 1, 1)) / 6, -Math.PI * Math.min(value * 2 + 1, 1) / 6); + else + setPosition(position); + } + return value; + } + inline private function getFloat(x:Int64):Float return x.high * 4294967296. + (x.low >> 0); inline private function getSamples(ms:Float):Int64 return Int64.fromFloat(Math.max(0, Math.min(ms / 1000 * parent.buffer.sampleRate, samples))); } \ No newline at end of file diff --git a/source/lime/media/AudioSource.hx b/source/lime/media/AudioSource.hx index 87f9e327..fd4c0078 100644 --- a/source/lime/media/AudioSource.hx +++ b/source/lime/media/AudioSource.hx @@ -80,6 +80,11 @@ class AudioSource **/ public var position(get, set):Vector4; + /** + The stereo pan of the audio source. + **/ + public var pan(get, set):Float; + /** The latency of the audio source. **/ @@ -229,6 +234,16 @@ class AudioSource return __backend.setPosition(value); } + @:noCompletion inline private function get_pan():Float + { + return __backend.getPan(); + } + + @:noCompletion inline private function set_pan(value:Float):Float + { + return __backend.setPan(value); + } + @:noCompletion inline private function get_latency():Float { return __backend.getLatency(); diff --git a/source/openfl/media/SoundChannel.hx b/source/openfl/media/SoundChannel.hx index e63d5909..f91320e8 100644 --- a/source/openfl/media/SoundChannel.hx +++ b/source/openfl/media/SoundChannel.hx @@ -273,13 +273,9 @@ import lime.media.openal.AL; if (__isValid) { #if lime + // TODO: implement SoundTransform.leftToRight, etc. with Native setAngles? __source.gain = volume; - - var position = __source.position; - position.x = pan; - position.z = -1 * Math.sqrt(1 - Math.pow(pan, 2)); - __source.position = position; - + __source.pan = pan; return value; #end }