|
|
|
@ -4,9 +4,15 @@ package aura.channels;
|
|
|
|
|
|
|
|
|
|
import js.Browser;
|
|
|
|
|
import js.html.AudioElement;
|
|
|
|
|
import js.html.audio.AudioContext;
|
|
|
|
|
import js.html.audio.ChannelSplitterNode;
|
|
|
|
|
import js.html.audio.ChannelMergerNode;
|
|
|
|
|
import js.html.audio.GainNode;
|
|
|
|
|
import js.html.audio.MediaElementAudioSourceNode;
|
|
|
|
|
import js.html.URL;
|
|
|
|
|
|
|
|
|
|
import kha.SystemImpl;
|
|
|
|
|
import kha.js.MobileWebAudio;
|
|
|
|
|
import kha.js.MobileWebAudioChannel;
|
|
|
|
|
|
|
|
|
|
import aura.threading.Message;
|
|
|
|
@ -30,20 +36,67 @@ import aura.types.AudioBuffer;
|
|
|
|
|
class Html5StreamChannel extends BaseChannel {
|
|
|
|
|
static final virtualChannels: Array<Html5StreamChannel> = [];
|
|
|
|
|
|
|
|
|
|
final audioContext: AudioContext;
|
|
|
|
|
final audioElement: AudioElement;
|
|
|
|
|
final source: MediaElementAudioSourceNode;
|
|
|
|
|
|
|
|
|
|
final gain: GainNode;
|
|
|
|
|
final leftGain: GainNode;
|
|
|
|
|
final rightGain: GainNode;
|
|
|
|
|
final attenuationGain: GainNode;
|
|
|
|
|
final splitter: ChannelSplitterNode;
|
|
|
|
|
final merger: ChannelMergerNode;
|
|
|
|
|
|
|
|
|
|
var virtualPosition: Float;
|
|
|
|
|
var lastUpdateTime: Float;
|
|
|
|
|
|
|
|
|
|
var dopplerRatio: Float = 1.0;
|
|
|
|
|
var pitch: Float = 1.0;
|
|
|
|
|
|
|
|
|
|
public function new(sound: kha.Sound, loop: Bool) {
|
|
|
|
|
audioContext = new AudioContext();
|
|
|
|
|
audioElement = Browser.document.createAudioElement();
|
|
|
|
|
source = audioContext.createMediaElementSource(audioElement);
|
|
|
|
|
|
|
|
|
|
final mimeType = #if kha_debug_html5 "audio/ogg" #else "audio/mp4" #end;
|
|
|
|
|
final blob = new js.html.Blob([sound.compressedData.getData()], {type: mimeType});
|
|
|
|
|
final soundData: js.lib.ArrayBuffer = sound.compressedData.getData();
|
|
|
|
|
final blob = new js.html.Blob([soundData], {type: mimeType});
|
|
|
|
|
|
|
|
|
|
// TODO: if removing channels, use revokeObjectUrl() ?
|
|
|
|
|
// see https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
|
|
|
|
|
audioElement.src = URL.createObjectURL(blob);
|
|
|
|
|
audioElement.loop = loop;
|
|
|
|
|
untyped audioElement.preservesPitch = false;
|
|
|
|
|
|
|
|
|
|
splitter = audioContext.createChannelSplitter(2);
|
|
|
|
|
leftGain = audioContext.createGain();
|
|
|
|
|
rightGain = audioContext.createGain();
|
|
|
|
|
attenuationGain = audioContext.createGain();
|
|
|
|
|
merger = audioContext.createChannelMerger(2);
|
|
|
|
|
gain = audioContext.createGain();
|
|
|
|
|
|
|
|
|
|
source.connect(splitter);
|
|
|
|
|
|
|
|
|
|
// The sound data needs to be decoded because `sounds.channels` returns `0`.
|
|
|
|
|
audioContext.decodeAudioData(soundData, function (buffer) {
|
|
|
|
|
// TODO: add more cases for Quad and 5.1 ? - https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Basic_concepts_behind_Web_Audio_API#audio_channels
|
|
|
|
|
switch (buffer.numberOfChannels) {
|
|
|
|
|
case 1:
|
|
|
|
|
splitter.connect(leftGain, 0);
|
|
|
|
|
splitter.connect(rightGain, 0);
|
|
|
|
|
case 2:
|
|
|
|
|
splitter.connect(leftGain, 0);
|
|
|
|
|
splitter.connect(rightGain, 1);
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
leftGain.connect(merger, 0, 0);
|
|
|
|
|
rightGain.connect(merger, 0, 1);
|
|
|
|
|
merger.connect(attenuationGain);
|
|
|
|
|
attenuationGain.connect(gain);
|
|
|
|
|
|
|
|
|
|
gain.connect(audioContext.destination);
|
|
|
|
|
|
|
|
|
|
if (isVirtual()) {
|
|
|
|
|
virtualChannels.push(this);
|
|
|
|
@ -130,15 +183,32 @@ class Html5StreamChannel extends BaseChannel {
|
|
|
|
|
switch (message.id) {
|
|
|
|
|
// Because we're using a HTML implementation here, we cannot use the
|
|
|
|
|
// LinearInterpolator parameters
|
|
|
|
|
case ChannelMessageID.PVolume: audioElement.volume = cast message.data;
|
|
|
|
|
case ChannelMessageID.PVolume: attenuationGain.gain.value = cast message.data;
|
|
|
|
|
case ChannelMessageID.PPitch:
|
|
|
|
|
pitch = cast message.data;
|
|
|
|
|
updatePlaybackRate();
|
|
|
|
|
case ChannelMessageID.PDopplerRatio:
|
|
|
|
|
case ChannelMessageID.PDstAttenuation:
|
|
|
|
|
dopplerRatio = cast message.data;
|
|
|
|
|
updatePlaybackRate();
|
|
|
|
|
case ChannelMessageID.PDstAttenuation: gain.gain.value = cast message.data;
|
|
|
|
|
case ChannelMessageID.PVolumeLeft: leftGain.gain.value = cast message.data;
|
|
|
|
|
case ChannelMessageID.PVolumeRight: rightGain.gain.value = cast message.data;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
super.parseMessage(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updatePlaybackRate() {
|
|
|
|
|
try {
|
|
|
|
|
audioElement.playbackRate = pitch * dopplerRatio;
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
// Ignore. Unfortunately some browsers only support a certain range
|
|
|
|
|
// of playback rates, but this is not explicitly specified, so there's
|
|
|
|
|
// not much we can do here.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -147,10 +217,50 @@ class Html5StreamChannel extends BaseChannel {
|
|
|
|
|
https://github.com/Kode/Kha/commit/12494b1112b64e4286b6a2fafc0f08462c1e7971
|
|
|
|
|
**/
|
|
|
|
|
class Html5MobileStreamChannel extends BaseChannel {
|
|
|
|
|
final audioContext: AudioContext;
|
|
|
|
|
final khaChannel: kha.js.MobileWebAudioChannel;
|
|
|
|
|
|
|
|
|
|
final leftGain: GainNode;
|
|
|
|
|
final rightGain: GainNode;
|
|
|
|
|
final attenuationGain: GainNode;
|
|
|
|
|
final splitter: ChannelSplitterNode;
|
|
|
|
|
final merger: ChannelMergerNode;
|
|
|
|
|
|
|
|
|
|
var dopplerRatio: Float = 1.0;
|
|
|
|
|
var pitch: Float = 1.0;
|
|
|
|
|
|
|
|
|
|
public function new(sound: kha.Sound, loop: Bool) {
|
|
|
|
|
audioContext = MobileWebAudio._context;
|
|
|
|
|
khaChannel = new kha.js.MobileWebAudioChannel(cast sound, loop);
|
|
|
|
|
|
|
|
|
|
@:privateAccess khaChannel.gain.disconnect(audioContext.destination);
|
|
|
|
|
@:privateAccess khaChannel.source.disconnect(@:privateAccess khaChannel.gain);
|
|
|
|
|
|
|
|
|
|
splitter = audioContext.createChannelSplitter(2);
|
|
|
|
|
leftGain = audioContext.createGain();
|
|
|
|
|
rightGain = audioContext.createGain();
|
|
|
|
|
merger = audioContext.createChannelMerger(2);
|
|
|
|
|
attenuationGain = audioContext.createGain();
|
|
|
|
|
|
|
|
|
|
@:privateAccess khaChannel.source.connect(splitter);
|
|
|
|
|
|
|
|
|
|
// TODO: add more cases for Quad and 5.1 ? - https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Basic_concepts_behind_Web_Audio_API#audio_channels
|
|
|
|
|
switch (sound.channels) {
|
|
|
|
|
case 1:
|
|
|
|
|
splitter.connect(leftGain, 0);
|
|
|
|
|
splitter.connect(rightGain, 0);
|
|
|
|
|
case 2:
|
|
|
|
|
splitter.connect(leftGain, 0);
|
|
|
|
|
splitter.connect(rightGain, 1);
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leftGain.connect(merger, 0, 0);
|
|
|
|
|
rightGain.connect(merger, 0, 1);
|
|
|
|
|
merger.connect(attenuationGain);
|
|
|
|
|
attenuationGain.connect(@:privateAccess khaChannel.gain);
|
|
|
|
|
|
|
|
|
|
@:privateAccess khaChannel.gain.connect(audioContext.destination);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function play(retrigger: Bool) {
|
|
|
|
@ -181,12 +291,26 @@ class Html5MobileStreamChannel extends BaseChannel {
|
|
|
|
|
// LinearInterpolator parameters
|
|
|
|
|
case ChannelMessageID.PVolume: khaChannel.volume = cast message.data;
|
|
|
|
|
case ChannelMessageID.PPitch:
|
|
|
|
|
pitch = cast message.data;
|
|
|
|
|
updatePlaybackRate();
|
|
|
|
|
case ChannelMessageID.PDopplerRatio:
|
|
|
|
|
case ChannelMessageID.PDstAttenuation:
|
|
|
|
|
dopplerRatio = cast message.data;
|
|
|
|
|
updatePlaybackRate();
|
|
|
|
|
case ChannelMessageID.PDstAttenuation: attenuationGain.gain.value = cast message.data;
|
|
|
|
|
case ChannelMessageID.PVolumeLeft: leftGain.gain.value = cast message.data;
|
|
|
|
|
case ChannelMessageID.PVolumeRight: rightGain.gain.value = cast message.data;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
super.parseMessage(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updatePlaybackRate() {
|
|
|
|
|
try {
|
|
|
|
|
@:privateAccess khaChannel.source.playbackRate.value = pitch * dopplerRatio;
|
|
|
|
|
}
|
|
|
|
|
catch (e) {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#end
|
|
|
|
|