198 lines
5.1 KiB
Haxe
198 lines
5.1 KiB
Haxe
|
package kha.audio2;
|
||
|
|
||
|
#if cpp
|
||
|
import sys.thread.Mutex;
|
||
|
#end
|
||
|
import haxe.ds.Vector;
|
||
|
|
||
|
class Audio1 {
|
||
|
static inline var channelCount: Int = 32;
|
||
|
static var soundChannels: Vector<AudioChannel>;
|
||
|
static var streamChannels: Vector<StreamChannel>;
|
||
|
|
||
|
static var internalSoundChannels: Vector<AudioChannel>;
|
||
|
static var internalStreamChannels: Vector<StreamChannel>;
|
||
|
static var sampleCache1: kha.arrays.Float32Array;
|
||
|
static var sampleCache2: kha.arrays.Float32Array;
|
||
|
static var lastAllocationCount: Int = 0;
|
||
|
|
||
|
#if cpp
|
||
|
static var mutex: Mutex;
|
||
|
#end
|
||
|
|
||
|
@:noCompletion
|
||
|
public static function _init(): Void {
|
||
|
#if cpp
|
||
|
mutex = new Mutex();
|
||
|
#end
|
||
|
soundChannels = new Vector<AudioChannel>(channelCount);
|
||
|
streamChannels = new Vector<StreamChannel>(channelCount);
|
||
|
internalSoundChannels = new Vector<AudioChannel>(channelCount);
|
||
|
internalStreamChannels = new Vector<StreamChannel>(channelCount);
|
||
|
sampleCache1 = new kha.arrays.Float32Array(512);
|
||
|
sampleCache2 = new kha.arrays.Float32Array(512);
|
||
|
lastAllocationCount = 0;
|
||
|
Audio.audioCallback = mix;
|
||
|
}
|
||
|
|
||
|
static inline function max(a: Float, b: Float): Float {
|
||
|
return a > b ? a : b;
|
||
|
}
|
||
|
|
||
|
static inline function min(a: Float, b: Float): Float {
|
||
|
return a < b ? a : b;
|
||
|
}
|
||
|
|
||
|
public static function mix(samplesBox: kha.internal.IntBox, buffer: Buffer): Void {
|
||
|
var samples = samplesBox.value;
|
||
|
if (sampleCache1.length < samples) {
|
||
|
if (Audio.disableGcInteractions) {
|
||
|
trace("Unexpected allocation request in audio thread.");
|
||
|
for (i in 0...samples) {
|
||
|
buffer.data.set(buffer.writeLocation, 0);
|
||
|
buffer.writeLocation += 1;
|
||
|
if (buffer.writeLocation >= buffer.size) {
|
||
|
buffer.writeLocation = 0;
|
||
|
}
|
||
|
}
|
||
|
lastAllocationCount = 0;
|
||
|
Audio.disableGcInteractions = false;
|
||
|
return;
|
||
|
}
|
||
|
sampleCache1 = new kha.arrays.Float32Array(samples * 2);
|
||
|
sampleCache2 = new kha.arrays.Float32Array(samples * 2);
|
||
|
lastAllocationCount = 0;
|
||
|
}
|
||
|
else {
|
||
|
if (lastAllocationCount > 100) {
|
||
|
Audio.disableGcInteractions = true;
|
||
|
}
|
||
|
else {
|
||
|
lastAllocationCount += 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i in 0...samples) {
|
||
|
sampleCache2[i] = 0;
|
||
|
}
|
||
|
|
||
|
#if cpp
|
||
|
mutex.acquire();
|
||
|
#end
|
||
|
for (i in 0...channelCount) {
|
||
|
internalSoundChannels[i] = soundChannels[i];
|
||
|
}
|
||
|
for (i in 0...channelCount) {
|
||
|
internalStreamChannels[i] = streamChannels[i];
|
||
|
}
|
||
|
#if cpp
|
||
|
mutex.release();
|
||
|
#end
|
||
|
|
||
|
for (channel in internalSoundChannels) {
|
||
|
if (channel == null || channel.finished)
|
||
|
continue;
|
||
|
channel.nextSamples(sampleCache1, samples, buffer.samplesPerSecond);
|
||
|
for (i in 0...samples) {
|
||
|
sampleCache2[i] += sampleCache1[i] * channel.volume;
|
||
|
}
|
||
|
}
|
||
|
for (channel in internalStreamChannels) {
|
||
|
if (channel == null || channel.finished)
|
||
|
continue;
|
||
|
channel.nextSamples(sampleCache1, samples, buffer.samplesPerSecond);
|
||
|
for (i in 0...samples) {
|
||
|
sampleCache2[i] += sampleCache1[i] * channel.volume;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i in 0...samples) {
|
||
|
buffer.data.set(buffer.writeLocation, max(min(sampleCache2[i], 1.0), -1.0));
|
||
|
buffer.writeLocation += 1;
|
||
|
if (buffer.writeLocation >= buffer.size) {
|
||
|
buffer.writeLocation = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static function play(sound: Sound, loop: Bool = false): kha.audio1.AudioChannel {
|
||
|
var channel: kha.audio2.AudioChannel = null;
|
||
|
if (Audio.samplesPerSecond != sound.sampleRate) {
|
||
|
channel = new ResamplingAudioChannel(loop, sound.sampleRate);
|
||
|
}
|
||
|
else {
|
||
|
#if sys_ios
|
||
|
channel = new ResamplingAudioChannel(loop, sound.sampleRate);
|
||
|
#else
|
||
|
channel = new AudioChannel(loop);
|
||
|
#end
|
||
|
}
|
||
|
channel.data = sound.uncompressedData;
|
||
|
var foundChannel = false;
|
||
|
|
||
|
#if cpp
|
||
|
mutex.acquire();
|
||
|
#end
|
||
|
for (i in 0...channelCount) {
|
||
|
if (soundChannels[i] == null || soundChannels[i].finished) {
|
||
|
soundChannels[i] = channel;
|
||
|
foundChannel = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#if cpp
|
||
|
mutex.release();
|
||
|
#end
|
||
|
|
||
|
return foundChannel ? channel : null;
|
||
|
}
|
||
|
|
||
|
public static function _playAgain(channel: kha.audio2.AudioChannel): Void {
|
||
|
#if cpp
|
||
|
mutex.acquire();
|
||
|
#end
|
||
|
for (i in 0...channelCount) {
|
||
|
if (soundChannels[i] == channel) {
|
||
|
soundChannels[i] = null;
|
||
|
}
|
||
|
}
|
||
|
for (i in 0...channelCount) {
|
||
|
if (soundChannels[i] == null || soundChannels[i].finished || soundChannels[i] == channel) {
|
||
|
soundChannels[i] = channel;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#if cpp
|
||
|
mutex.release();
|
||
|
#end
|
||
|
}
|
||
|
|
||
|
public static function stream(sound: Sound, loop: Bool = false): kha.audio1.AudioChannel {
|
||
|
{
|
||
|
// try to use hardware accelerated audio decoding
|
||
|
var hardwareChannel = Audio.stream(sound, loop);
|
||
|
if (hardwareChannel != null)
|
||
|
return hardwareChannel;
|
||
|
}
|
||
|
|
||
|
var channel: StreamChannel = new StreamChannel(sound.compressedData, loop);
|
||
|
var foundChannel = false;
|
||
|
|
||
|
#if cpp
|
||
|
mutex.acquire();
|
||
|
#end
|
||
|
for (i in 0...channelCount) {
|
||
|
if (streamChannels[i] == null || streamChannels[i].finished) {
|
||
|
streamChannels[i] = channel;
|
||
|
foundChannel = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#if cpp
|
||
|
mutex.release();
|
||
|
#end
|
||
|
|
||
|
return foundChannel ? channel : null;
|
||
|
}
|
||
|
}
|