Skip to content

Commit 7d186d2

Browse files
committed
Stable Streaming Audio
1 parent 86b3fb3 commit 7d186d2

File tree

2 files changed

+39
-63
lines changed

2 files changed

+39
-63
lines changed

project.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@
188188
<haxedef name="SOFTCODED_CLASSES" if="SOFTCODED_CLASSES" />
189189
<haxedef name="USE_ADAPTED_ASSETS" if="USE_ADAPTED_ASSETS" />
190190
<haxedef name="openfl_dpi_aware" if="openfl_dpi_aware" />
191-
<haxedef name="audio_stream_async" unless="web" />
192191

193192
<!-- _________________________________ Custom _______________________________ -->
194193

source/lime/_internal/backend/native/NativeAudioSource.hx

Lines changed: 39 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package lime._internal.backend.native;
22

3+
import sys.thread.Thread;
4+
import sys.thread.Mutex;
5+
36
import haxe.Timer;
47
import haxe.Int64;
58

9+
import lime.app.Application;
610
import lime.media.openal.AL;
711
import lime.media.openal.ALBuffer;
812
import lime.media.openal.ALSource;
@@ -23,11 +27,6 @@ import lime.utils.ArrayBuffer;
2327
import lime.utils.ArrayBufferView.TypedArrayType;
2428
import lime.utils.ArrayBufferView;
2529

26-
#if audio_stream_async
27-
import sys.thread.Thread;
28-
import sys.thread.Mutex;
29-
#end
30-
3130
#if !lime_debug
3231
@:fileXml('tags="haxe,release"')
3332
@:noDebug
@@ -43,7 +42,6 @@ class NativeAudioSource {
4342
public static var STREAM_MAX_BUFFERS:Int = 8; // how much limit of a buffers can be used for streamed audios, must be higher than minimum.
4443
public static var STREAM_FLUSH_BUFFERS:Int = 3; // how much buffers can it play.
4544
public static var STREAM_PROCESS_BUFFERS:Int = 2; // how much buffers can be processed in a frequency tick.
46-
public static var STREAM_TIMER_CHECK_MS:Int = 100; // determines how milliseconds to update the buffers if available.
4745
public static var MAX_POOL_BUFFERS:Int = 32; // how much buffers for the pool to hold.
4846

4947
public static var moreFormatsSupported:Null<Bool>;
@@ -89,6 +87,14 @@ class NativeAudioSource {
8987

9088
inline private static function getFloat(x:Int64):Float return x.high * 4294967296. + (x.low >> 0);
9189

90+
inline private static function gc() {
91+
#if neko
92+
neko.vm.Gc.run(false);
93+
#elseif cpp
94+
cpp.vm.Gc.run(false);
95+
#end
96+
}
97+
9298
// Backward Compatibility Variables
9399
var handle(get, set):ALSource; inline function get_handle() return source; inline function set_handle(v) return source = v;
94100
var timer(get, set):Timer; inline function get_timer() return completeTimer; inline function set_timer(v) return completeTimer = v;
@@ -125,19 +131,14 @@ class NativeAudioSource {
125131
var arrayType:TypedArrayType;
126132
var loopPoints:Array<Int>; // In Samples
127133

128-
#if audio_stream_async
129134
static var threadRunning:Bool = false;
130135
static var streamSources:Array<NativeAudioSource> = [];
131136
static var queuedStreamSources:Array<NativeAudioSource> = [];
132137

133-
static var streamHandlerTimer:Timer;
134138
static var streamMutex:Mutex = new Mutex();
135139
static var streamThread:Thread;
136140

137141
var streamRemove:Bool;
138-
#else
139-
var streamTimer:Timer;
140-
#end
141142

142143
var bufferLength:Int; // Size in bytes for current streamed audio buffers.
143144
var requestBuffers:Int;
@@ -162,10 +163,8 @@ class NativeAudioSource {
162163
}
163164

164165
public function dispose() {
165-
#if audio_stream_async
166166
streamMutex.acquire();
167167
removeStream();
168-
#end
169168

170169
stop();
171170
disposed = true;
@@ -201,12 +200,11 @@ class NativeAudioSource {
201200
}
202201

203202
completeTimer = null;
204-
#if !audio_stream_async streamTimer = null; #end
205203

206204
bufferTimes = null;
207205
bufferLengths = null;
208206

209-
#if audio_stream_async streamMutex.release(); #end
207+
streamMutex.release();
210208
}
211209

212210
public function init() {
@@ -225,17 +223,15 @@ class NativeAudioSource {
225223
public function resetBuffer() {
226224
if (parent.buffer == null) return;
227225

228-
#if audio_stream_async
229226
streamMutex.acquire();
230227
removeStream();
231-
#end
232228

233229
stop();
234230

235231
if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
236232
else AL.sourcei(source, AL.BUFFER, AL.NONE);
237233

238-
#if audio_stream_async streamMutex.release(); #end
234+
streamMutex.release();
239235

240236
final audioBuffer = parent.buffer;
241237
channels = audioBuffer.channels;
@@ -456,7 +452,7 @@ class NativeAudioSource {
456452
function snapBuffersToTime(time:Float, force:Bool) {
457453
if (source == null || parent.buffer == null || parent.buffer.__srcVorbisFile == null) return;
458454

459-
#if audio_stream_async streamMutex.acquire(); #end
455+
streamMutex.acquire();
460456

461457
final sec = time / 1000;
462458
if (!force) {
@@ -466,8 +462,7 @@ class NativeAudioSource {
466462
{
467463
skipBuffers(i - STREAM_MAX_BUFFERS + requestBuffers);
468464
AL.sourcei(source, AL.SAMPLE_OFFSET, Math.floor((sec - bufferTime) * sampleRate));
469-
#if audio_stream_async streamMutex.release(); #end
470-
return;
465+
return streamMutex.release();
471466
}
472467
}
473468

@@ -479,10 +474,9 @@ class NativeAudioSource {
479474
requestBuffers = queuedBuffers = streamLoops = nextBuffer = 0;
480475
fillBuffers(STREAM_MIN_BUFFERS);
481476
flushBuffers();
482-
#if audio_stream_async streamMutex.release(); #end
477+
streamMutex.release();
483478
}
484479

485-
#if audio_stream_async
486480
static function streamThreadRun() {
487481
var i:Int, source:NativeAudioSource, process:Int, v:Int;
488482

@@ -500,39 +494,48 @@ class NativeAudioSource {
500494
if ((process = (v = STREAM_MAX_BUFFERS - source.requestBuffers) > process ? process : v) > 0) source.fillBuffers(process);
501495
}
502496
streamMutex.release();
497+
gc();
503498
}
504499

505500
threadRunning = false;
506501
}
507502

508-
static function streamHandlerRun() {
509-
if (!streamMutex.tryAcquire()) return;
503+
static function streamUpdate(_:Int) {
504+
final acquired = streamMutex.tryAcquire();
510505

511506
var i = queuedStreamSources.length, source:NativeAudioSource;
512507
while (i-- > 0) streamSources.push(queuedStreamSources[i]);
513508
queuedStreamSources.resize(0);
514509

515510
i = streamSources.length;
516511
while (i-- > 0) {
517-
if ((source = streamSources[i]).streamRemove || source.source == null)
518-
source.removeStream();
512+
if ((source = streamSources[i]).streamRemove) {
513+
if (acquired) source.removeStream();
514+
else continue;
515+
}
516+
else if (source.source == null) {
517+
if (acquired) source.removeStream();
518+
else source.stopStream();
519+
}
519520
else {
520-
source.skipBuffers(AL.getSourcei(source.source, AL.BUFFERS_PROCESSED));
521+
if (acquired) source.skipBuffers(AL.getSourcei(source.source, AL.BUFFERS_PROCESSED));
521522
source.flushBuffers();
522-
523523
if (AL.getSourcei(source.source, AL.SOURCE_STATE) == AL.STOPPED) {
524524
AL.sourcePlay(source.source);
525525
source.updateCompleteTimer();
526526
}
527-
if (source.streamEnded) source.removeStream();
528527
}
529528
}
530529

531-
streamMutex.release();
532-
533-
if (streamSources.length == 0) streamHandlerTimer.stop();
534-
else if (threadRunning || (threadRunning = (streamThread = Thread.create(streamThreadRun)) != null))
535-
streamThread.sendMessage(streamSources.length);
530+
if (acquired) {
531+
streamMutex.release();
532+
if (streamSources.length == 0) {
533+
Application.current.onUpdate.remove(streamUpdate);
534+
if (threadRunning) streamThread.sendMessage(0);
535+
}
536+
else if (threadRunning || (threadRunning = (streamThread = Thread.create(streamThreadRun)) != null))
537+
streamThread.sendMessage(streamSources.length);
538+
}
536539
}
537540

538541
function removeStream() {
@@ -550,36 +553,10 @@ class NativeAudioSource {
550553
streamRemove = false;
551554
if (!queuedStreamSources.contains(this) && !streamSources.contains(this)) {
552555
queuedStreamSources.push(this);
553-
if (streamHandlerTimer == null || !streamHandlerTimer.mRunning)
554-
streamHandlerTimer = resetTimer(streamHandlerTimer, STREAM_TIMER_CHECK_MS, streamHandlerRun);
555-
}
556-
}
557-
#else
558-
function streamRun() {
559-
if (source == null || parent.buffer == null || parent.buffer.__srcVorbisFile == null)
560-
return streamTimer.stop();
561-
562-
skipBuffers(AL.getSourcei(source, AL.BUFFERS_PROCESSED));
563-
564-
var process = requestBuffers < STREAM_MIN_BUFFERS ? STREAM_MIN_BUFFERS - requestBuffers : 0, v = STREAM_MAX_BUFFERS - requestBuffers;
565-
process = STREAM_PROCESS_BUFFERS > process ? STREAM_PROCESS_BUFFERS : process;
566-
if ((process = v > process ? process : v) > 0) fillBuffers(process);
567-
flushBuffers();
568-
569-
if (AL.getSourcei(source, AL.SOURCE_STATE) == AL.STOPPED) {
570-
AL.sourcePlay(source);
571-
updateCompleteTimer();
556+
if (!Application.current.onUpdate.has(streamUpdate)) Application.current.onUpdate.add(streamUpdate);
572557
}
573-
if (streamEnded) streamTimer.stop();
574558
}
575559

576-
function stopStream() if (streamTimer != null) streamTimer.stop();
577-
578-
function resetStream()
579-
if (streamTimer == null || !streamTimer.mRunning)
580-
streamTimer = resetTimer(streamTimer, STREAM_TIMER_CHECK_MS, streamRun);
581-
#end
582-
583560
function timer_onRun() {
584561
final pitch = getPitch();
585562
var timeRemaining = (getLength() - getCurrentTime()) / pitch;

0 commit comments

Comments
 (0)