forked from LeenkxTeam/LNXSDK
Update Aura
This commit is contained in:
@ -30,15 +30,15 @@ class LoaderImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function loadSoundFromDescription(desc: Dynamic, done: kha.Sound->Void, failed: AssetError->Void) {
|
public static function loadSoundFromDescription(desc: Dynamic, done: kha.Sound->Void, failed: AssetError->Void) {
|
||||||
var sound = Krom.loadSound(desc.files[0]);
|
var sound = new kha.krom.Sound(desc.files[0]);
|
||||||
if (sound == null) {
|
if (sound.uncompressedData == null) {
|
||||||
failed({
|
failed({
|
||||||
url: desc.files.join(","),
|
url: desc.files.join(","),
|
||||||
error: "Could not load sound(s)",
|
error: "Could not load sound(s)",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
done(new kha.krom.Sound(Bytes.ofData(sound)));
|
done(sound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,24 +2,28 @@ package kha.krom;
|
|||||||
|
|
||||||
import haxe.io.Bytes;
|
import haxe.io.Bytes;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
class Sound extends kha.Sound {
|
class Sound extends kha.Sound {
|
||||||
public function new(bytes: Bytes) {
|
public function new(filename: String) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
var sound = Krom.loadSound(filename);
|
||||||
|
if (sound != null) {
|
||||||
|
var bytes = Bytes.ofData(sound.buffer);
|
||||||
var count = Std.int(bytes.length / 4);
|
var count = Std.int(bytes.length / 4);
|
||||||
uncompressedData = new kha.arrays.Float32Array(count);
|
uncompressedData = new kha.arrays.Float32Array(count);
|
||||||
for (i in 0...count) {
|
for (i in 0...count) {
|
||||||
uncompressedData[i] = bytes.getFloat(i * 4);
|
uncompressedData[i] = bytes.getFloat(i * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
compressedData = null;
|
this.sampleRate = sound.sampleRate;
|
||||||
|
this.channels = sound.channels;
|
||||||
|
this.length = sound.length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function uncompress(done: Void->Void): Void {
|
override public function uncompress(done: Void->Void): Void {
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function unload(): Void {
|
|
||||||
super.unload();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,7 +68,7 @@ class Sound implements Resource {
|
|||||||
var soundBytes = output.getBytes();
|
var soundBytes = output.getBytes();
|
||||||
var count = Std.int(soundBytes.length / 4);
|
var count = Std.int(soundBytes.length / 4);
|
||||||
if (header.channel == 1) {
|
if (header.channel == 1) {
|
||||||
length = count / kha.audio2.Audio.samplesPerSecond; // header.sampleRate;
|
length = count / header.sampleRate;
|
||||||
uncompressedData = new kha.arrays.Float32Array(count * 2);
|
uncompressedData = new kha.arrays.Float32Array(count * 2);
|
||||||
for (i in 0...count) {
|
for (i in 0...count) {
|
||||||
uncompressedData[i * 2 + 0] = soundBytes.getFloat(i * 4);
|
uncompressedData[i * 2 + 0] = soundBytes.getFloat(i * 4);
|
||||||
@ -76,7 +76,7 @@ class Sound implements Resource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
length = count / 2 / kha.audio2.Audio.samplesPerSecond; // header.sampleRate;
|
length = count / 2 / header.sampleRate;
|
||||||
uncompressedData = new kha.arrays.Float32Array(count);
|
uncompressedData = new kha.arrays.Float32Array(count);
|
||||||
for (i in 0...count) {
|
for (i in 0...count) {
|
||||||
uncompressedData[i] = soundBytes.getFloat(i * 4);
|
uncompressedData[i] = soundBytes.getFloat(i * 4);
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
// Keep this file so that the headers are included in the compilation
|
// Keep this file so that the headers are included in the compilation
|
||||||
#include "hl/aura/math/FFT.h"
|
#include "hl/aura/math/fft.h"
|
||||||
#include "hl/aura/types/complex_array.h"
|
#include "hl/aura/types/complex_array.h"
|
||||||
|
|||||||
@ -2,27 +2,27 @@
|
|||||||
|
|
||||||
#include <hl.h>
|
#include <hl.h>
|
||||||
|
|
||||||
//#include <aura/types/_ComplexArray/HL_ComplexArrayImpl.h>
|
#include <aura/types/_ComplexArray/HL_ComplexArrayImpl.h>
|
||||||
|
|
||||||
#include "hl/aura/aurahl.h"
|
#include "hl/aura/aurahl.h"
|
||||||
#include "common_c/math/fft.h"
|
#include "common_c/math/fft.h"
|
||||||
#include "common_c/types/complex_t.h"
|
#include "common_c/types/complex_t.h"
|
||||||
|
|
||||||
//HL_PRIM void AURA_HL_FUNC(ditfft2)(aura__types___ComplexArray__HL_ComplexArrayImpl time_array, int t, aura__types___ComplexArray__HL_ComplexArrayImpl freq_array, int f, int n, int step, bool inverse) {
|
HL_PRIM void AURA_HL_FUNC(ditfft2)(aura__types___ComplexArray__HL_ComplexArrayImpl time_array, int t, aura__types___ComplexArray__HL_ComplexArrayImpl freq_array, int f, int n, int step, bool inverse) {
|
||||||
// const aura_complex_t *times = (aura_complex_t*) time_array->self;
|
const aura_complex_t *times = (aura_complex_t*) time_array->self;
|
||||||
// aura_complex_t *freqs = (aura_complex_t*) freq_array->self;
|
aura_complex_t *freqs = (aura_complex_t*) freq_array->self;
|
||||||
|
|
||||||
/// aura_ditfft2(times, t, freqs, f, n, step, inverse);
|
aura_ditfft2(times, t, freqs, f, n, step, inverse);
|
||||||
//}
|
}
|
||||||
|
|
||||||
//HL_PRIM void AURA_HL_FUNC(ditfft2_iterative)(aura__types___ComplexArray__HL_ComplexArrayImpl time_array, aura__types___ComplexArray__HL_ComplexArrayImpl freq_array, int n, bool inverse, aura__types___ComplexArray__HL_ComplexArrayImpl exp_rotation_step_table) {
|
HL_PRIM void AURA_HL_FUNC(ditfft2_iterative)(aura__types___ComplexArray__HL_ComplexArrayImpl time_array, aura__types___ComplexArray__HL_ComplexArrayImpl freq_array, int n, bool inverse, aura__types___ComplexArray__HL_ComplexArrayImpl exp_rotation_step_table) {
|
||||||
// const aura_complex_t *times = (aura_complex_t*) time_array->self;
|
const aura_complex_t *times = (aura_complex_t*) time_array->self;
|
||||||
// aura_complex_t *freqs = (aura_complex_t*) freq_array->self;
|
aura_complex_t *freqs = (aura_complex_t*) freq_array->self;
|
||||||
|
|
||||||
// const aura_complex_t *exp_lut = (aura_complex_t*) exp_rotation_step_table->self;
|
const aura_complex_t *exp_lut = (aura_complex_t*) exp_rotation_step_table->self;
|
||||||
|
|
||||||
// aura_ditfft2_iterative(times, freqs, n, inverse, exp_lut);
|
aura_ditfft2_iterative(times, freqs, n, inverse, exp_lut);
|
||||||
//}
|
}
|
||||||
|
|
||||||
//DEFINE_PRIM(_VOID, ditfft2, _BYTES _I32 _BYTES _I32 _I32 _I32 _BOOL)
|
DEFINE_PRIM(_VOID, ditfft2, _BYTES _I32 _BYTES _I32 _I32 _I32 _BOOL)
|
||||||
//DEFINE_PRIM(_VOID, ditfft2_iterative, _BYTES _BYTES _I32 _BOOL _BYTES)
|
DEFINE_PRIM(_VOID, ditfft2_iterative, _BYTES _BYTES _I32 _BOOL _BYTES)
|
||||||
|
|||||||
@ -53,6 +53,10 @@ class Aura {
|
|||||||
|
|
||||||
static final hrtfs = new Map<String, HRTF>();
|
static final hrtfs = new Map<String, HRTF>();
|
||||||
|
|
||||||
|
#if (kha_html5 || kha_debug_html5)
|
||||||
|
public static var audioContext: js.html.audio.AudioContext;
|
||||||
|
#end
|
||||||
|
|
||||||
public static function init(?options: AuraOptions) {
|
public static function init(?options: AuraOptions) {
|
||||||
sampleRate = kha.audio2.Audio.samplesPerSecond;
|
sampleRate = kha.audio2.Audio.samplesPerSecond;
|
||||||
assert(Critical, sampleRate != 0, "sampleRate must not be 0!");
|
assert(Critical, sampleRate != 0, "sampleRate must not be 0!");
|
||||||
@ -64,11 +68,15 @@ class Aura {
|
|||||||
|
|
||||||
BufferCache.init();
|
BufferCache.init();
|
||||||
|
|
||||||
// Sample buffer to prevent allocation
|
#if (kha_html5 || kha_debug_html5)
|
||||||
final initialBufferSize = 4096;
|
if (kha.SystemImpl.mobile) {
|
||||||
if (!BufferCache.getBuffer(TFloat32Array, p_samplesBuffer, 1, initialBufferSize)) {
|
audioContext = kha.js.MobileWebAudio._context;
|
||||||
trace('CRITICAL: Failed to allocate initial sample buffer during Aura.init()!');
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
audioContext = new js.html.audio.AudioContext();
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
// Create a few preconfigured mix channels
|
// Create a few preconfigured mix channels
|
||||||
masterChannel = createMixChannel("master");
|
masterChannel = createMixChannel("master");
|
||||||
createMixChannel("music").setMixChannel(masterChannel);
|
createMixChannel("music").setMixChannel(masterChannel);
|
||||||
@ -134,16 +142,31 @@ class Aura {
|
|||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
function onChannelCountInitialized() {
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
if (onProgress != null) {
|
if (onProgress != null) {
|
||||||
onProgress(count, length, soundName);
|
onProgress(count, length, soundName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count == length) {
|
if (count == length) {
|
||||||
done();
|
done();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (kha_html5 || kha_debug_html5)
|
||||||
|
if (kha.SystemImpl.mobile) {
|
||||||
|
// Mobile web audio channels are always decoded and
|
||||||
|
// the channel count is set by Kha afterwards
|
||||||
|
onChannelCountInitialized();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// HACK: Kha does not set sound.channel for compressed
|
||||||
|
// sounds on non-mobile html5 targets, so do it manually
|
||||||
|
aura.channels.Html5StreamChannel.initializeChannelCount(sound, onChannelCountInitialized);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
onChannelCountInitialized();
|
||||||
|
#end
|
||||||
}, (error: kha.AssetError) -> { onLoadingError(error, failed, soundName); });
|
}, (error: kha.AssetError) -> { onLoadingError(error, failed, soundName); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +343,7 @@ class Aura {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if (kha_html5 || kha_debug_html5)
|
#if (kha_html5 || kha_debug_html5)
|
||||||
final newChannel = kha.SystemImpl.mobile ? new Html5MobileStreamChannel(sound, loop) : new Html5StreamChannel(sound, loop);
|
final newChannel = kha.SystemImpl.mobile ? new Html5MobileStreamChannel(sound, loop, cast(mixChannelHandle.channel, MixChannel)) : new Html5StreamChannel(sound, loop, cast(mixChannelHandle.channel, MixChannel));
|
||||||
#else
|
#else
|
||||||
final khaChannel: Null<kha.audio1.AudioChannel> = kha.audio2.Audio1.stream(sound, loop);
|
final khaChannel: Null<kha.audio1.AudioChannel> = kha.audio2.Audio1.stream(sound, loop);
|
||||||
if (khaChannel == null) {
|
if (khaChannel == null) {
|
||||||
|
|||||||
@ -8,6 +8,9 @@ import aura.threading.Message;
|
|||||||
import aura.types.AudioBuffer;
|
import aura.types.AudioBuffer;
|
||||||
import aura.utils.Interpolator.LinearInterpolator;
|
import aura.utils.Interpolator.LinearInterpolator;
|
||||||
import aura.utils.MathUtils;
|
import aura.utils.MathUtils;
|
||||||
|
#if (kha_html5 || kha_debug_html5)
|
||||||
|
import js.html.audio.GainNode;
|
||||||
|
#end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Main-thread handle to an audio channel in the audio thread.
|
Main-thread handle to an audio channel in the audio thread.
|
||||||
@ -90,6 +93,7 @@ class BaseChannelHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mixChannelHandle == null) {
|
if (mixChannelHandle == null) {
|
||||||
|
channel.cleanUp();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +109,12 @@ class BaseChannelHandle {
|
|||||||
final success = @:privateAccess mixChannelHandle.addInputChannel(this);
|
final success = @:privateAccess mixChannelHandle.addInputChannel(this);
|
||||||
if (success) {
|
if (success) {
|
||||||
parentHandle = mixChannelHandle;
|
parentHandle = mixChannelHandle;
|
||||||
|
#if (kha_html5 || kha_debug_html5)
|
||||||
|
if (channel is MixChannel) {
|
||||||
|
channel.gain.disconnect();
|
||||||
|
channel.gain.connect(@:privateAccess parentHandle.getMixChannel().gain);
|
||||||
|
}
|
||||||
|
#end
|
||||||
} else {
|
} else {
|
||||||
parentHandle = null;
|
parentHandle = null;
|
||||||
}
|
}
|
||||||
@ -164,6 +174,10 @@ abstract class BaseChannel {
|
|||||||
var paused: Bool = false;
|
var paused: Bool = false;
|
||||||
var finished: Bool = true;
|
var finished: Bool = true;
|
||||||
|
|
||||||
|
#if (kha_html5 || kha_debug_html5)
|
||||||
|
public var gain: GainNode;
|
||||||
|
#end
|
||||||
|
|
||||||
abstract function nextSamples(requestedSamples: AudioBuffer, sampleRate: Hertz): Void;
|
abstract function nextSamples(requestedSamples: AudioBuffer, sampleRate: Hertz): Void;
|
||||||
|
|
||||||
abstract function play(retrigger: Bool): Void;
|
abstract function play(retrigger: Bool): Void;
|
||||||
@ -218,6 +232,8 @@ abstract class BaseChannel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanUp() {}
|
||||||
|
|
||||||
function parseMessage(message: Message) {
|
function parseMessage(message: Message) {
|
||||||
switch (message.id) {
|
switch (message.id) {
|
||||||
case ChannelMessageID.Play: play(cast message.data);
|
case ChannelMessageID.Play: play(cast message.data);
|
||||||
|
|||||||
@ -10,14 +10,21 @@ import js.html.audio.ChannelMergerNode;
|
|||||||
import js.html.audio.GainNode;
|
import js.html.audio.GainNode;
|
||||||
import js.html.audio.MediaElementAudioSourceNode;
|
import js.html.audio.MediaElementAudioSourceNode;
|
||||||
import js.html.URL;
|
import js.html.URL;
|
||||||
|
import js.lib.ArrayBuffer;
|
||||||
|
|
||||||
import kha.SystemImpl;
|
import kha.SystemImpl;
|
||||||
import kha.js.MobileWebAudio;
|
import kha.js.MobileWebAudio;
|
||||||
import kha.js.MobileWebAudioChannel;
|
import kha.js.MobileWebAudioChannel;
|
||||||
|
|
||||||
|
import aura.Aura;
|
||||||
|
import aura.format.audio.OggVorbisReader;
|
||||||
import aura.threading.Message;
|
import aura.threading.Message;
|
||||||
import aura.types.AudioBuffer;
|
import aura.types.AudioBuffer;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
using aura.format.BytesExtension;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Channel dedicated for streaming playback on html5.
|
Channel dedicated for streaming playback on html5.
|
||||||
|
|
||||||
@ -36,16 +43,16 @@ import aura.types.AudioBuffer;
|
|||||||
class Html5StreamChannel extends BaseChannel {
|
class Html5StreamChannel extends BaseChannel {
|
||||||
static final virtualChannels: Array<Html5StreamChannel> = [];
|
static final virtualChannels: Array<Html5StreamChannel> = [];
|
||||||
|
|
||||||
final audioContext: AudioContext;
|
var audioContext: AudioContext;
|
||||||
final audioElement: AudioElement;
|
var audioElement: AudioElement;
|
||||||
final source: MediaElementAudioSourceNode;
|
var source: MediaElementAudioSourceNode;
|
||||||
|
|
||||||
final gain: GainNode;
|
var masterGain: GainNode;
|
||||||
final leftGain: GainNode;
|
var leftGain: GainNode;
|
||||||
final rightGain: GainNode;
|
var rightGain: GainNode;
|
||||||
final attenuationGain: GainNode;
|
var attenuationGain: GainNode;
|
||||||
final splitter: ChannelSplitterNode;
|
var splitter: ChannelSplitterNode;
|
||||||
final merger: ChannelMergerNode;
|
var merger: ChannelMergerNode;
|
||||||
|
|
||||||
var virtualPosition: Float;
|
var virtualPosition: Float;
|
||||||
var lastUpdateTime: Float;
|
var lastUpdateTime: Float;
|
||||||
@ -53,13 +60,13 @@ class Html5StreamChannel extends BaseChannel {
|
|||||||
var dopplerRatio: Float = 1.0;
|
var dopplerRatio: Float = 1.0;
|
||||||
var pitch: Float = 1.0;
|
var pitch: Float = 1.0;
|
||||||
|
|
||||||
public function new(sound: kha.Sound, loop: Bool) {
|
public function new(sound: kha.Sound, loop: Bool, parentChannel: MixChannel) {
|
||||||
audioContext = new AudioContext();
|
audioContext = Aura.audioContext;
|
||||||
audioElement = Browser.document.createAudioElement();
|
audioElement = Browser.document.createAudioElement();
|
||||||
source = audioContext.createMediaElementSource(audioElement);
|
source = audioContext.createMediaElementSource(audioElement);
|
||||||
|
|
||||||
final mimeType = #if kha_debug_html5 "audio/ogg" #else "audio/mp4" #end;
|
final mimeType = sound.compressedData.isByteMagic(0, "OggS") ? "audio/ogg" : "audio/mp4";
|
||||||
final soundData: js.lib.ArrayBuffer = sound.compressedData.getData();
|
final soundData: ArrayBuffer = sound.compressedData.getData();
|
||||||
final blob = new js.html.Blob([soundData], {type: mimeType});
|
final blob = new js.html.Blob([soundData], {type: mimeType});
|
||||||
|
|
||||||
// TODO: if removing channels, use revokeObjectUrl() ?
|
// TODO: if removing channels, use revokeObjectUrl() ?
|
||||||
@ -67,20 +74,21 @@ class Html5StreamChannel extends BaseChannel {
|
|||||||
audioElement.src = URL.createObjectURL(blob);
|
audioElement.src = URL.createObjectURL(blob);
|
||||||
audioElement.loop = loop;
|
audioElement.loop = loop;
|
||||||
untyped audioElement.preservesPitch = false;
|
untyped audioElement.preservesPitch = false;
|
||||||
|
audioElement.addEventListener("ended", () -> {
|
||||||
|
stop();
|
||||||
|
});
|
||||||
|
|
||||||
splitter = audioContext.createChannelSplitter(2);
|
splitter = audioContext.createChannelSplitter(2);
|
||||||
leftGain = audioContext.createGain();
|
leftGain = audioContext.createGain();
|
||||||
rightGain = audioContext.createGain();
|
rightGain = audioContext.createGain();
|
||||||
attenuationGain = audioContext.createGain();
|
attenuationGain = audioContext.createGain();
|
||||||
merger = audioContext.createChannelMerger(2);
|
merger = audioContext.createChannelMerger(2);
|
||||||
gain = audioContext.createGain();
|
masterGain = audioContext.createGain();
|
||||||
|
|
||||||
source.connect(splitter);
|
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
|
// 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) {
|
switch (sound.channels) {
|
||||||
case 1:
|
case 1:
|
||||||
splitter.connect(leftGain, 0);
|
splitter.connect(leftGain, 0);
|
||||||
splitter.connect(rightGain, 0);
|
splitter.connect(rightGain, 0);
|
||||||
@ -88,15 +96,15 @@ class Html5StreamChannel extends BaseChannel {
|
|||||||
splitter.connect(leftGain, 0);
|
splitter.connect(leftGain, 0);
|
||||||
splitter.connect(rightGain, 1);
|
splitter.connect(rightGain, 1);
|
||||||
default:
|
default:
|
||||||
|
throw 'Unsupported channel count: ${sound.channels}';
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
leftGain.connect(merger, 0, 0);
|
leftGain.connect(merger, 0, 0);
|
||||||
rightGain.connect(merger, 0, 1);
|
rightGain.connect(merger, 0, 1);
|
||||||
merger.connect(attenuationGain);
|
merger.connect(attenuationGain);
|
||||||
attenuationGain.connect(gain);
|
attenuationGain.connect(masterGain);
|
||||||
|
|
||||||
gain.connect(audioContext.destination);
|
masterGain.connect(parentChannel.gain);
|
||||||
|
|
||||||
if (isVirtual()) {
|
if (isVirtual()) {
|
||||||
virtualChannels.push(this);
|
virtualChannels.push(this);
|
||||||
@ -177,20 +185,45 @@ class Html5StreamChannel extends BaseChannel {
|
|||||||
finished = true;
|
finished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Clean up Web Audio nodes. Called automatically when `BaseChannelHandle.setMixChannel(null)` is used.
|
||||||
|
**/
|
||||||
|
override function cleanUp() {
|
||||||
|
source.disconnect();
|
||||||
|
splitter.disconnect();
|
||||||
|
leftGain.disconnect();
|
||||||
|
rightGain.disconnect();
|
||||||
|
merger.disconnect();
|
||||||
|
attenuationGain.disconnect();
|
||||||
|
masterGain.disconnect();
|
||||||
|
audioElement.pause();
|
||||||
|
audioElement.src = "";
|
||||||
|
URL.revokeObjectURL(audioElement.src);
|
||||||
|
|
||||||
|
source = null;
|
||||||
|
splitter = null;
|
||||||
|
leftGain = null;
|
||||||
|
rightGain = null;
|
||||||
|
merger = null;
|
||||||
|
attenuationGain = null;
|
||||||
|
masterGain = null;
|
||||||
|
audioElement = null;
|
||||||
|
}
|
||||||
|
|
||||||
function nextSamples(requestedSamples: AudioBuffer, sampleRate: Hertz) {}
|
function nextSamples(requestedSamples: AudioBuffer, sampleRate: Hertz) {}
|
||||||
|
|
||||||
override function parseMessage(message: Message) {
|
override function parseMessage(message: Message) {
|
||||||
switch (message.id) {
|
switch (message.id) {
|
||||||
// Because we're using a HTML implementation here, we cannot use the
|
// Because we're using a HTML implementation here, we cannot use the
|
||||||
// LinearInterpolator parameters
|
// LinearInterpolator parameters
|
||||||
case ChannelMessageID.PVolume: attenuationGain.gain.value = cast message.data;
|
case ChannelMessageID.PVolume: masterGain.gain.value = cast message.data;
|
||||||
case ChannelMessageID.PPitch:
|
case ChannelMessageID.PPitch:
|
||||||
pitch = cast message.data;
|
pitch = cast message.data;
|
||||||
updatePlaybackRate();
|
updatePlaybackRate();
|
||||||
case ChannelMessageID.PDopplerRatio:
|
case ChannelMessageID.PDopplerRatio:
|
||||||
dopplerRatio = cast message.data;
|
dopplerRatio = cast message.data;
|
||||||
updatePlaybackRate();
|
updatePlaybackRate();
|
||||||
case ChannelMessageID.PDstAttenuation: gain.gain.value = cast message.data;
|
case ChannelMessageID.PDstAttenuation: attenuationGain.gain.value = cast message.data;
|
||||||
case ChannelMessageID.PVolumeLeft: leftGain.gain.value = cast message.data;
|
case ChannelMessageID.PVolumeLeft: leftGain.gain.value = cast message.data;
|
||||||
case ChannelMessageID.PVolumeRight: rightGain.gain.value = cast message.data;
|
case ChannelMessageID.PVolumeRight: rightGain.gain.value = cast message.data;
|
||||||
|
|
||||||
@ -217,24 +250,23 @@ class Html5StreamChannel extends BaseChannel {
|
|||||||
https://github.com/Kode/Kha/commit/12494b1112b64e4286b6a2fafc0f08462c1e7971
|
https://github.com/Kode/Kha/commit/12494b1112b64e4286b6a2fafc0f08462c1e7971
|
||||||
**/
|
**/
|
||||||
class Html5MobileStreamChannel extends BaseChannel {
|
class Html5MobileStreamChannel extends BaseChannel {
|
||||||
final audioContext: AudioContext;
|
var audioContext: AudioContext;
|
||||||
final khaChannel: kha.js.MobileWebAudioChannel;
|
var khaChannel: kha.js.MobileWebAudioChannel;
|
||||||
|
var parentChannel: MixChannel;
|
||||||
|
|
||||||
final leftGain: GainNode;
|
var leftGain: GainNode;
|
||||||
final rightGain: GainNode;
|
var rightGain: GainNode;
|
||||||
final attenuationGain: GainNode;
|
var attenuationGain: GainNode;
|
||||||
final splitter: ChannelSplitterNode;
|
var splitter: ChannelSplitterNode;
|
||||||
final merger: ChannelMergerNode;
|
var merger: ChannelMergerNode;
|
||||||
|
|
||||||
var dopplerRatio: Float = 1.0;
|
var dopplerRatio: Float = 1.0;
|
||||||
var pitch: Float = 1.0;
|
var pitch: Float = 1.0;
|
||||||
|
|
||||||
public function new(sound: kha.Sound, loop: Bool) {
|
public function new(sound: kha.Sound, loop: Bool, pc: MixChannel) {
|
||||||
audioContext = MobileWebAudio._context;
|
audioContext = Aura.audioContext;
|
||||||
khaChannel = new kha.js.MobileWebAudioChannel(cast sound, loop);
|
khaChannel = new kha.js.MobileWebAudioChannel(cast sound, loop);
|
||||||
|
parentChannel = pc;
|
||||||
@:privateAccess khaChannel.gain.disconnect(audioContext.destination);
|
|
||||||
@:privateAccess khaChannel.source.disconnect(@:privateAccess khaChannel.gain);
|
|
||||||
|
|
||||||
splitter = audioContext.createChannelSplitter(2);
|
splitter = audioContext.createChannelSplitter(2);
|
||||||
leftGain = audioContext.createGain();
|
leftGain = audioContext.createGain();
|
||||||
@ -242,8 +274,6 @@ class Html5MobileStreamChannel extends BaseChannel {
|
|||||||
merger = audioContext.createChannelMerger(2);
|
merger = audioContext.createChannelMerger(2);
|
||||||
attenuationGain = audioContext.createGain();
|
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
|
// 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) {
|
switch (sound.channels) {
|
||||||
case 1:
|
case 1:
|
||||||
@ -253,6 +283,7 @@ class Html5MobileStreamChannel extends BaseChannel {
|
|||||||
splitter.connect(leftGain, 0);
|
splitter.connect(leftGain, 0);
|
||||||
splitter.connect(rightGain, 1);
|
splitter.connect(rightGain, 1);
|
||||||
default:
|
default:
|
||||||
|
throw 'Unsupported channel count: ${sound.channels}';
|
||||||
}
|
}
|
||||||
|
|
||||||
leftGain.connect(merger, 0, 0);
|
leftGain.connect(merger, 0, 0);
|
||||||
@ -260,14 +291,19 @@ class Html5MobileStreamChannel extends BaseChannel {
|
|||||||
merger.connect(attenuationGain);
|
merger.connect(attenuationGain);
|
||||||
attenuationGain.connect(@:privateAccess khaChannel.gain);
|
attenuationGain.connect(@:privateAccess khaChannel.gain);
|
||||||
|
|
||||||
@:privateAccess khaChannel.gain.connect(audioContext.destination);
|
reconnectKhaChannelNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function play(retrigger: Bool) {
|
public function play(retrigger: Bool) {
|
||||||
if (retrigger) {
|
if (retrigger) {
|
||||||
khaChannel.position = 0;
|
khaChannel.position = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@:privateAccess khaChannel.source.onended = null;
|
||||||
khaChannel.play();
|
khaChannel.play();
|
||||||
|
// `MobileWebAudioChannel` recreates a 'source' when `khaChannel.play()` is called
|
||||||
|
// Reconnect 'source' and 'gain' to the proper nodes
|
||||||
|
reconnectKhaChannelNodes();
|
||||||
|
|
||||||
paused = false;
|
paused = false;
|
||||||
finished = false;
|
finished = false;
|
||||||
@ -283,6 +319,30 @@ class Html5MobileStreamChannel extends BaseChannel {
|
|||||||
finished = true;
|
finished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Clean up Web Audio nodes. Called automatically when `BaseChannelHandle.setMixChannel(null)` is used.
|
||||||
|
**/
|
||||||
|
override function cleanUp() {
|
||||||
|
@:privateAccess khaChannel.source.onended = null;
|
||||||
|
@:privateAccess khaChannel.source.disconnect();
|
||||||
|
splitter.disconnect();
|
||||||
|
leftGain.disconnect();
|
||||||
|
rightGain.disconnect();
|
||||||
|
merger.disconnect();
|
||||||
|
attenuationGain.disconnect();
|
||||||
|
@:privateAccess khaChannel.gain.disconnect();
|
||||||
|
khaChannel.stop();
|
||||||
|
|
||||||
|
@:privateAccess khaChannel.gain = null;
|
||||||
|
@:privateAccess khaChannel.source = null;
|
||||||
|
splitter = null;
|
||||||
|
leftGain = null;
|
||||||
|
rightGain = null;
|
||||||
|
merger = null;
|
||||||
|
attenuationGain = null;
|
||||||
|
khaChannel = null;
|
||||||
|
}
|
||||||
|
|
||||||
function nextSamples(requestedSamples: AudioBuffer, sampleRate: Hertz) {}
|
function nextSamples(requestedSamples: AudioBuffer, sampleRate: Hertz) {}
|
||||||
|
|
||||||
override function parseMessage(message: Message) {
|
override function parseMessage(message: Message) {
|
||||||
@ -311,6 +371,49 @@ class Html5MobileStreamChannel extends BaseChannel {
|
|||||||
}
|
}
|
||||||
catch (e) {}
|
catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reconnectKhaChannelNodes() {
|
||||||
|
@:privateAccess khaChannel.gain.disconnect();
|
||||||
|
@:privateAccess khaChannel.source.disconnect();
|
||||||
|
@:privateAccess khaChannel.source.connect(splitter);
|
||||||
|
@:privateAccess khaChannel.source.onended = stop;
|
||||||
|
@:privateAccess khaChannel.gain.connect(parentChannel.gain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeChannelCount(sound: kha.Sound, done: Void->Void) {
|
||||||
|
/*
|
||||||
|
Peek into the file to detect the file format, Kha sadly does not expose
|
||||||
|
this information.
|
||||||
|
|
||||||
|
Using `Reflect.field(kha.Assets.sounds, soundName + "Description").files`
|
||||||
|
(i.e. the data in files.json generated by Khamake) would introduce a
|
||||||
|
dependency on Kha internals: A `kha.Sound` can be backed by multiple
|
||||||
|
exported files and Kha's `LoaderImpl` decides on the order in which
|
||||||
|
those files are tried to be loaded.
|
||||||
|
*/
|
||||||
|
final isOgg = sound.compressedData.isByteMagic(0, "OggS");
|
||||||
|
if (isOgg) {
|
||||||
|
final oggReader = new OggVorbisReader(sound.compressedData);
|
||||||
|
sound.channels = oggReader.getNumChannels();
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/*
|
||||||
|
In case of other formats, try to let the JS runtime decode the
|
||||||
|
entire sound data.
|
||||||
|
|
||||||
|
HACK: decodeAudioData() detaches the array buffer but requires a
|
||||||
|
non-detached buffer, so a clone is made to ensure that the array
|
||||||
|
buffer of sound.compressedData is never detached and can still be
|
||||||
|
used by other code.
|
||||||
|
*/
|
||||||
|
final soundDataClone: ArrayBuffer = sound.compressedData.getData().slice(0);
|
||||||
|
kha.audio2.Audio._context.decodeAudioData(soundDataClone, function (buffer) {
|
||||||
|
sound.channels = buffer.numberOfChannels;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#end
|
#end
|
||||||
|
|||||||
@ -6,6 +6,11 @@ import haxe.ds.Vector;
|
|||||||
import sys.thread.Mutex;
|
import sys.thread.Mutex;
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
#if (kha_html5 || kha_debug_html5)
|
||||||
|
import aura.Aura;
|
||||||
|
import js.html.audio.AudioContext;
|
||||||
|
#end
|
||||||
|
|
||||||
import aura.channels.BaseChannel.BaseChannelHandle;
|
import aura.channels.BaseChannel.BaseChannelHandle;
|
||||||
import aura.threading.BufferCache;
|
import aura.threading.BufferCache;
|
||||||
import aura.threading.Message;
|
import aura.threading.Message;
|
||||||
@ -89,7 +94,17 @@ class MixChannel extends BaseChannel {
|
|||||||
**/
|
**/
|
||||||
var inputChannelsCopy: Vector<BaseChannel>;
|
var inputChannelsCopy: Vector<BaseChannel>;
|
||||||
|
|
||||||
|
#if (kha_html5 || kha_debug_html5)
|
||||||
|
var audioContext: AudioContext;
|
||||||
|
#end
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
|
#if (kha_html5 || kha_debug_html5)
|
||||||
|
audioContext = Aura.audioContext;
|
||||||
|
gain = audioContext.createGain();
|
||||||
|
gain.connect(audioContext.destination);
|
||||||
|
#end
|
||||||
|
|
||||||
inputChannels = new Vector<BaseChannel>(channelSize);
|
inputChannels = new Vector<BaseChannel>(channelSize);
|
||||||
|
|
||||||
// Make sure super.isPlayable() is true until we find better semantics
|
// Make sure super.isPlayable() is true until we find better semantics
|
||||||
@ -296,4 +311,18 @@ class MixChannel extends BaseChannel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (kha_html5 || kha_debug_html5)
|
||||||
|
//TODO: add the rest of the messages for effects or create a separate `Html5MixChannel` class?
|
||||||
|
override function parseMessage(message: Message) {
|
||||||
|
switch (message.id) {
|
||||||
|
// Because we're using a HTML implementation here, we cannot use the
|
||||||
|
// LinearInterpolator parameters
|
||||||
|
case ChannelMessageID.PVolume: gain.gain.value = cast message.data;
|
||||||
|
|
||||||
|
default:
|
||||||
|
super.parseMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,8 +44,12 @@ class StreamChannel extends BaseChannel {
|
|||||||
}
|
}
|
||||||
final khaBuffer = p_khaBuffer.get();
|
final khaBuffer = p_khaBuffer.get();
|
||||||
|
|
||||||
khaChannel.nextSamples(khaBuffer, requestedSamples.channelLength, sampleRate);
|
khaChannel.nextSamples(khaBuffer, requestedSamples.numChannels * requestedSamples.channelLength, sampleRate);
|
||||||
requestedSamples.deinterleaveFromFloat32Array(khaBuffer, requestedSamples.numChannels);
|
requestedSamples.deinterleaveFromFloat32Array(khaBuffer, requestedSamples.numChannels);
|
||||||
|
|
||||||
|
if (khaChannel.finished) {
|
||||||
|
finished = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override function parseMessage(message: Message) {
|
override function parseMessage(message: Message) {
|
||||||
|
|||||||
@ -43,7 +43,7 @@ abstract class Panner extends DSP {
|
|||||||
public function new(handle: BaseChannelHandle) {
|
public function new(handle: BaseChannelHandle) {
|
||||||
this.inUse = true; // Don't allow using panners with addInsert()
|
this.inUse = true; // Don't allow using panners with addInsert()
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
this.handle.channel.panner = this;
|
handle.channel.panner = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline function setHandle(handle: BaseChannelHandle) {
|
public inline function setHandle(handle: BaseChannelHandle) {
|
||||||
@ -52,7 +52,7 @@ abstract class Panner extends DSP {
|
|||||||
}
|
}
|
||||||
reset3D();
|
reset3D();
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
this.handle.channel.panner = this;
|
handle.channel.panner = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -61,10 +61,13 @@ class StereoPanner extends Panner {
|
|||||||
|
|
||||||
public inline function setBalance(balance: Balance) {
|
public inline function setBalance(balance: Balance) {
|
||||||
this._balance = balance;
|
this._balance = balance;
|
||||||
|
|
||||||
final volumeLeft = Math.sqrt(~balance);
|
final volumeLeft = Math.sqrt(~balance);
|
||||||
final volumeRight = Math.sqrt(balance);
|
final volumeRight = Math.sqrt(balance);
|
||||||
|
|
||||||
sendMessage({ id: StereoPannerMessageID.PVolumeLeft, data: volumeLeft });
|
sendMessage({ id: StereoPannerMessageID.PVolumeLeft, data: volumeLeft });
|
||||||
sendMessage({ id: StereoPannerMessageID.PVolumeRight, data: volumeRight });
|
sendMessage({ id: StereoPannerMessageID.PVolumeRight, data: volumeRight });
|
||||||
|
|
||||||
#if (kha_html5 || kha_debug_html5)
|
#if (kha_html5 || kha_debug_html5)
|
||||||
handle.channel.sendMessage({ id: ChannelMessageID.PVolumeLeft, data: volumeLeft });
|
handle.channel.sendMessage({ id: ChannelMessageID.PVolumeLeft, data: volumeLeft });
|
||||||
handle.channel.sendMessage({ id: ChannelMessageID.PVolumeRight, data: volumeRight });
|
handle.channel.sendMessage({ id: ChannelMessageID.PVolumeRight, data: volumeRight });
|
||||||
|
|||||||
17
lib/aura/Sources/aura/format/BytesExtension.hx
Normal file
17
lib/aura/Sources/aura/format/BytesExtension.hx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package aura.format;
|
||||||
|
|
||||||
|
import haxe.io.Bytes;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Variant of `aura.format.InputExtension.isByteMagic()` for `haxe.io.Bytes`.
|
||||||
|
**/
|
||||||
|
inline function isByteMagic(bytes: Bytes, position: Int, magicASCII: String): Bool {
|
||||||
|
var match = true;
|
||||||
|
for (i in 0...magicASCII.length) {
|
||||||
|
match = match && bytes.get(position + i) == magicASCII.fastCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
@ -3,6 +3,8 @@ package aura.format;
|
|||||||
import haxe.Int64;
|
import haxe.Int64;
|
||||||
import haxe.io.Input;
|
import haxe.io.Input;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
inline function readInt64(inp: Input): Int64 {
|
inline function readInt64(inp: Input): Int64 {
|
||||||
final first = inp.readInt32();
|
final first = inp.readInt32();
|
||||||
final second = inp.readInt32();
|
final second = inp.readInt32();
|
||||||
@ -19,3 +21,24 @@ inline function readUInt32(inp: Input): Int64 {
|
|||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Platform- and encoding-independent way of matching the input with an ASCII
|
||||||
|
magic string. This function does not consider the input endianess, it is
|
||||||
|
assumed that the order of characters in `magicASCII` matches the byte order
|
||||||
|
in the input stream.
|
||||||
|
|
||||||
|
- `inp.readString(len, haxe.io.Encoding.UTF8)` does not work if the input
|
||||||
|
streams contains data that can be interpreted as multi-byte characters.
|
||||||
|
|
||||||
|
- `inp.readString(len, haxe.io.Encoding.RawNative)` does not yield
|
||||||
|
platform-indepent results.
|
||||||
|
**/
|
||||||
|
inline function isByteMagic(inp: Input, magicASCII: String): Bool {
|
||||||
|
var match = true;
|
||||||
|
for (i in 0...magicASCII.length) {
|
||||||
|
match = match && inp.readByte() == magicASCII.fastCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|||||||
86
lib/aura/Sources/aura/format/audio/OggVorbisReader.hx
Normal file
86
lib/aura/Sources/aura/format/audio/OggVorbisReader.hx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
Ogg layout:
|
||||||
|
https://en.wikipedia.org/wiki/Ogg#Page_structure
|
||||||
|
|
||||||
|
Vorbis layout:
|
||||||
|
https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2
|
||||||
|
https://wiki.xiph.org/OggVorbis
|
||||||
|
**/
|
||||||
|
|
||||||
|
package aura.format.audio;
|
||||||
|
|
||||||
|
import haxe.io.Bytes;
|
||||||
|
import haxe.io.BytesInput;
|
||||||
|
|
||||||
|
using aura.format.InputExtension;
|
||||||
|
|
||||||
|
class OggVorbisReader {
|
||||||
|
|
||||||
|
static inline final OGG_PAGE_HEADER_TYPE_BEG_OF_STREAM = 2;
|
||||||
|
static inline final VORBIS_PACKET_TYPE_IDENTIFICATION = 1;
|
||||||
|
|
||||||
|
final inp: BytesInput;
|
||||||
|
|
||||||
|
final firstSegmentPosition = 0;
|
||||||
|
final segmentTable: Bytes;
|
||||||
|
|
||||||
|
public inline function new(bytes: Bytes) {
|
||||||
|
this.inp = new BytesInput(bytes);
|
||||||
|
inp.bigEndian = false;
|
||||||
|
|
||||||
|
if (!inp.isByteMagic("OggS")) {
|
||||||
|
throw "Cannot read .ogg file, file does not start with 'OggS' magic";
|
||||||
|
}
|
||||||
|
|
||||||
|
inp.position += 1; // Skip version
|
||||||
|
|
||||||
|
final oggHeaderType = inp.readByte();
|
||||||
|
if (oggHeaderType != OGG_PAGE_HEADER_TYPE_BEG_OF_STREAM) {
|
||||||
|
throw "Cannot read .ogg file, first header type was expected to be 'Beginning Of Stream'";
|
||||||
|
}
|
||||||
|
|
||||||
|
inp.position += 8; // Skip granule position
|
||||||
|
inp.position += 4; // Skip bitstream serial number
|
||||||
|
|
||||||
|
final pageSequenceNumber = inp.readUInt32();
|
||||||
|
if (pageSequenceNumber != 0) {
|
||||||
|
throw "Cannot read .ogg file, first page sequence number was expected to be 0";
|
||||||
|
}
|
||||||
|
|
||||||
|
inp.position += 4; // Skip checksum (for now)
|
||||||
|
|
||||||
|
final numPageSegments = inp.readByte();
|
||||||
|
if (numPageSegments == 0) {
|
||||||
|
throw "Cannot read .ogg file, first page has no segments";
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentTable = Bytes.alloc(numPageSegments);
|
||||||
|
inp.readFullBytes(segmentTable, 0, numPageSegments);
|
||||||
|
|
||||||
|
firstSegmentPosition = inp.position;
|
||||||
|
|
||||||
|
final packetType = inp.readByte();
|
||||||
|
if (packetType != VORBIS_PACKET_TYPE_IDENTIFICATION) {
|
||||||
|
throw "Cannot read .ogg file, Vorbis identification header expected";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inp.isByteMagic("vorbis")) {
|
||||||
|
throw "Cannot read .ogg file, only Ogg Vorbis files are supported";
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2, version must be 0
|
||||||
|
final version = inp.readUInt32();
|
||||||
|
if (version != 0) {
|
||||||
|
throw "Cannot read .ogg file, Vorbis version field expected to be 0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNumChannels(): Int {
|
||||||
|
inp.position = firstSegmentPosition + 1 + 6 + 4; // Skip packet type + vorbis identifier + version
|
||||||
|
|
||||||
|
final numChannels = inp.readByte();
|
||||||
|
assert(Critical, numChannels > 0);
|
||||||
|
|
||||||
|
return numChannels;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,7 +30,7 @@ abstract Vec3(FastVector3) from FastVector3 to FastVector3 {
|
|||||||
return new FastVector4(this.x, this.y, this.z);
|
return new FastVector4(this.x, this.y, this.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (AURA_WITH_IRON || leenkx)
|
#if (AURA_WITH_IRON || armory)
|
||||||
@:from
|
@:from
|
||||||
public static inline function fromIronVec3(v: iron.math.Vec3): Vec3{
|
public static inline function fromIronVec3(v: iron.math.Vec3): Vec3{
|
||||||
return new FastVector3(v.x, v.y, v.z);
|
return new FastVector3(v.x, v.y, v.z);
|
||||||
|
|||||||
@ -7,12 +7,13 @@ const targetsHL = ["windows-hl", "linux-hl", "macos-hl", "osx-hl", "android-hl",
|
|||||||
const targetsCPP = ["windows", "linux", "macos", "osx"];
|
const targetsCPP = ["windows", "linux", "macos", "osx"];
|
||||||
const targetsHTML5 = ["html5", "debug-html5"];
|
const targetsHTML5 = ["html5", "debug-html5"];
|
||||||
|
|
||||||
function addBackends(project) {
|
async function addBackends(project) {
|
||||||
project.localLibraryPath = "Backends";
|
//project.localLibraryPath = "Backends";
|
||||||
|
|
||||||
const isHL = targetsHL.indexOf(Project.platform) >= 0;
|
const isHL = targetsHL.indexOf(Project.platform) >= 0;
|
||||||
|
|
||||||
if (isHL) {
|
if (isHL) {
|
||||||
|
await project.addProject("Backends/hl");
|
||||||
project.addDefine("AURA_BACKEND_HL");
|
project.addDefine("AURA_BACKEND_HL");
|
||||||
console.log("[Aura] Using HL/C backend");
|
console.log("[Aura] Using HL/C backend");
|
||||||
}
|
}
|
||||||
@ -31,7 +32,7 @@ async function main() {
|
|||||||
project.addSources('Sources');
|
project.addSources('Sources');
|
||||||
|
|
||||||
if (process.argv.indexOf("--aura-no-backend") == -1) {
|
if (process.argv.indexOf("--aura-no-backend") == -1) {
|
||||||
addBackends(project);
|
await addBackends(project);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
project.addDefine("AURA_NO_BACKEND");
|
project.addDefine("AURA_NO_BACKEND");
|
||||||
|
|||||||
Reference in New Issue
Block a user