Skip to content

Commit 82fb0bc

Browse files
committed
Stable audio_stream_async
1 parent e6bcb14 commit 82fb0bc

File tree

1 file changed

+71
-45
lines changed

1 file changed

+71
-45
lines changed

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

Lines changed: 71 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class NativeAudioSource {
4141
public static var STREAM_BUFFER_SAMPLES:Int = 0x2000; // how much buffers will be generating every frequency (doesnt have to be pow of 2?).
4242
public static var STREAM_MIN_BUFFERS:Int = 2; // how much buffers can a stream hold on minimum or starting.
4343
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.
44+
public static var STREAM_FLUSH_BUFFERS:Int = 3; // how much buffers can it play.
4445
public static var STREAM_PROCESS_BUFFERS:Int = 2; // how much buffers can be processed in a frequency tick.
4546
public static var STREAM_TIMER_CHECK_MS:Int = 100; // determines how milliseconds to update the buffers if available.
4647
public static var MAX_POOL_BUFFERS:Int = 32; // how much buffers for the pool to hold.
@@ -88,18 +89,6 @@ class NativeAudioSource {
8889

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

91-
#if audio_stream_async
92-
static var threadRunning:Bool = false;
93-
static var streamSources:Array<NativeAudioSource> = [];
94-
static var queuedStreamSources:Array<NativeAudioSource> = [];
95-
96-
static var streamHandlerTimer:Timer;
97-
static var streamMutex:Mutex = new Mutex();
98-
static var streamThread:Thread;
99-
100-
var streamRemove:Bool;
101-
#end
102-
10392
// Backward Compatibility Variables
10493
var handle(get, set):ALSource; inline function get_handle() return source; inline function set_handle(v) return source = v;
10594
var timer(get, set):Timer; inline function get_timer() return completeTimer; inline function set_timer(v) return completeTimer = v;
@@ -128,7 +117,6 @@ class NativeAudioSource {
128117
var dataLength:Int;
129118
var duration:Float;
130119

131-
#if !audio_stream_async var streamTimer:Timer; #end
132120
var completeTimer:Timer;
133121
var source:ALSource;
134122
var buffer:ALBuffer;
@@ -137,6 +125,20 @@ class NativeAudioSource {
137125
var arrayType:TypedArrayType;
138126
var loopPoints:Array<Int>; // In Samples
139127

128+
#if audio_stream_async
129+
static var threadRunning:Bool = false;
130+
static var streamSources:Array<NativeAudioSource> = [];
131+
static var queuedStreamSources:Array<NativeAudioSource> = [];
132+
133+
static var streamHandlerTimer:Timer;
134+
static var streamMutex:Mutex = new Mutex();
135+
static var streamThread:Thread;
136+
137+
var streamRemove:Bool;
138+
#else
139+
var streamTimer:Timer;
140+
#end
141+
140142
var bufferLength:Int; // Size in bytes for current streamed audio buffers.
141143
var requestBuffers:Int;
142144
var queuedBuffers:Int;
@@ -160,6 +162,11 @@ class NativeAudioSource {
160162
}
161163

162164
public function dispose() {
165+
#if audio_stream_async
166+
streamMutex.acquire();
167+
removeStream();
168+
#end
169+
163170
stop();
164171
disposed = true;
165172

@@ -168,7 +175,9 @@ class NativeAudioSource {
168175
anglesArray = null;
169176

170177
if (source != null) {
171-
AL.sourcei(source, AL.BUFFER, AL.NONE);
178+
if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
179+
else AL.sourcei(source, AL.BUFFER, AL.NONE);
180+
172181
AL.deleteSource(source);
173182
source = null;
174183
}
@@ -196,6 +205,8 @@ class NativeAudioSource {
196205

197206
bufferTimes = null;
198207
bufferLengths = null;
208+
209+
#if audio_stream_async streamMutex.release(); #end
199210
}
200211

201212
public function init() {
@@ -213,9 +224,18 @@ class NativeAudioSource {
213224

214225
public function resetBuffer() {
215226
if (parent.buffer == null) return;
227+
228+
#if audio_stream_async
229+
streamMutex.acquire();
230+
removeStream();
231+
#end
232+
216233
stop();
217234

218-
AL.sourcei(source, AL.BUFFER, AL.NONE);
235+
if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
236+
else AL.sourcei(source, AL.BUFFER, AL.NONE);
237+
238+
#if audio_stream_async streamMutex.release(); #end
219239

220240
final audioBuffer = parent.buffer;
221241
channels = audioBuffer.channels;
@@ -256,7 +276,7 @@ class NativeAudioSource {
256276
final length = STREAM_BUFFER_SAMPLES * channels;
257277
bufferLength = length * wordSize;
258278

259-
if (buffers == null) buffers = AL.genBuffers(STREAM_MAX_BUFFERS);
279+
if (buffers == null) buffers = AL.genBuffers(STREAM_FLUSH_BUFFERS);
260280
if (bufferDatas == null) {
261281
bufferDatas = [];
262282
bufferTimes = [];
@@ -388,7 +408,7 @@ class NativeAudioSource {
388408
}
389409
}
390410
catch (e:haxe.Exception) {
391-
trace('NativeAudioSource readToBufferData Bug! error: ${e.message} | ${e.stack}, streamEnded: $streamEnded, total: $total, n: $n');
411+
trace('NativeAudioSource readToBufferData Bug! error: ${e.message} | ${e.stack.toString()}, streamEnded: $streamEnded, total: $total, n: $n');
392412
return result;
393413
}
394414

@@ -402,9 +422,7 @@ class NativeAudioSource {
402422
function fillBuffers(n:Int) {
403423
final max = STREAM_MAX_BUFFERS - 1;
404424
var i:Int, j:Int, data:ArrayBufferView, pcm:Int64, decoded:Int;
405-
while (n-- > 0 && requestBuffers < STREAM_MAX_BUFFERS && !streamEnded
406-
&& (decoded = readToBufferData(data = bufferDatas[i = max - requestBuffers], pcm = streamTell())) > 0)
407-
{
425+
while (n-- > 0 && !streamEnded && (decoded = readToBufferData(data = bufferDatas[i = max - requestBuffers], pcm = streamTell())) > 0) {
408426
j = i;
409427
while (i < max) {
410428
bufferDatas[i] = bufferDatas[++j];
@@ -421,12 +439,12 @@ class NativeAudioSource {
421439

422440
inline function flushBuffers() {
423441
var i = STREAM_MAX_BUFFERS - (requestBuffers - queuedBuffers);
424-
while (queuedBuffers < requestBuffers) {
442+
while (queuedBuffers < STREAM_FLUSH_BUFFERS && queuedBuffers < requestBuffers) {
425443
AL.bufferData(buffers[nextBuffer], format, bufferDatas[i], bufferLengths[i], sampleRate);
426444
AL.sourceQueueBuffer(source, buffers[nextBuffer]);
427-
if (++nextBuffer == STREAM_MAX_BUFFERS) nextBuffer = 0;
428-
i++;
445+
if (++nextBuffer == STREAM_FLUSH_BUFFERS) nextBuffer = 0;
429446
queuedBuffers++;
447+
i++;
430448
}
431449
}
432450

@@ -438,24 +456,23 @@ class NativeAudioSource {
438456
function snapBuffersToTime(time:Float, force:Bool) {
439457
if (source == null || parent.buffer == null || parent.buffer.__srcVorbisFile == null) return;
440458

459+
#if audio_stream_async streamMutex.acquire(); #end
460+
441461
final sec = time / 1000;
442462
if (!force) {
443463
var bufferTime:Float;
444-
for (i in (STREAM_MAX_BUFFERS - queuedBuffers)...STREAM_MAX_BUFFERS)
464+
for (i in (STREAM_MAX_BUFFERS - requestBuffers)...(STREAM_MAX_BUFFERS - STREAM_MIN_BUFFERS))
445465
if (sec >= (bufferTime = bufferTimes[i]) && sec < bufferTime + (bufferLengths[i] / wordSize / channels / sampleRate))
446466
{
447-
#if audio_stream_async streamMutex.acquire(); #end
448-
skipBuffers(i - STREAM_MAX_BUFFERS + queuedBuffers);
467+
skipBuffers(i - STREAM_MAX_BUFFERS + requestBuffers);
449468
AL.sourcei(source, AL.SAMPLE_OFFSET, Math.floor((sec - bufferTime) * sampleRate));
450-
fillBuffers(STREAM_MIN_BUFFERS - STREAM_MAX_BUFFERS + i);
451469
#if audio_stream_async streamMutex.release(); #end
452-
return flushBuffers();
470+
return;
453471
}
454472
}
455473

456474
AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
457475

458-
#if audio_stream_async streamMutex.acquire(); #end
459476
streamEnded = false;
460477
streamSeek(Int64.fromFloat(sec * sampleRate));
461478

@@ -467,17 +484,20 @@ class NativeAudioSource {
467484

468485
#if audio_stream_async
469486
static function streamThreadRun() {
470-
var i:Int, source:NativeAudioSource, process:Int;
487+
var i:Int, source:NativeAudioSource, process:Int, v:Int;
471488

472489
while ((i = Thread.readMessage(true)) != 0) {
473490
streamMutex.acquire();
474491
while (i-- > 0) {
475-
if ((source = streamSources[i]).parent.buffer == null) {
492+
if ((source = streamSources[i]).streamRemove) continue;
493+
else if (source.parent.buffer == null) {
476494
source.stopStream();
477495
continue;
478496
}
497+
479498
process = source.requestBuffers < STREAM_MIN_BUFFERS ? STREAM_MIN_BUFFERS - source.requestBuffers : 0;
480-
source.fillBuffers(STREAM_PROCESS_BUFFERS > process ? STREAM_PROCESS_BUFFERS : process);
499+
process = STREAM_PROCESS_BUFFERS > process ? STREAM_PROCESS_BUFFERS : process;
500+
if ((process = (v = STREAM_MAX_BUFFERS - source.requestBuffers) > process ? process : v) > 0) source.fillBuffers(process);
481501
}
482502
streamMutex.release();
483503
}
@@ -488,9 +508,14 @@ class NativeAudioSource {
488508
static function streamHandlerRun() {
489509
if (!streamMutex.tryAcquire()) return;
490510

491-
var i = streamSources.length, source:NativeAudioSource;
511+
var i = queuedStreamSources.length, source:NativeAudioSource;
512+
while (i-- > 0) streamSources.push(queuedStreamSources[i]);
513+
queuedStreamSources.resize(0);
514+
515+
i = streamSources.length;
492516
while (i-- > 0) {
493-
if ((source = streamSources[i]).source == null) source.stopStream();
517+
if ((source = streamSources[i]).streamRemove || source.source == null)
518+
source.removeStream();
494519
else {
495520
source.skipBuffers(AL.getSourcei(source.source, AL.BUFFERS_PROCESSED));
496521
source.flushBuffers();
@@ -499,32 +524,32 @@ class NativeAudioSource {
499524
AL.sourcePlay(source.source);
500525
source.updateCompleteTimer();
501526
}
502-
if (source.streamEnded) source.stopStream();
527+
if (source.streamEnded) source.removeStream();
503528
}
504-
505-
if (source.streamRemove) streamSources.remove(source);
506529
}
507530

508-
i = queuedStreamSources.length;
509-
while (i-- > 0) streamSources.push(queuedStreamSources[i]);
510-
queuedStreamSources.resize(0);
511-
512531
streamMutex.release();
513532

514533
if (streamSources.length == 0) streamHandlerTimer.stop();
515534
else if (threadRunning || (threadRunning = (streamThread = Thread.create(streamThreadRun)) != null))
516535
streamThread.sendMessage(streamSources.length);
517536
}
518537

519-
function stopStream() {
538+
function removeStream() {
539+
streamRemove = false;
520540
queuedStreamSources.remove(this);
541+
streamSources.remove(this);
542+
}
543+
544+
function stopStream() {
521545
streamRemove = true;
546+
queuedStreamSources.remove(this);
522547
}
523548

524549
function resetStream() {
550+
streamRemove = false;
525551
if (!queuedStreamSources.contains(this) && !streamSources.contains(this)) {
526552
queuedStreamSources.push(this);
527-
streamRemove = false;
528553
if (streamHandlerTimer == null || !streamHandlerTimer.mRunning)
529554
streamHandlerTimer = resetTimer(streamHandlerTimer, STREAM_TIMER_CHECK_MS, streamHandlerRun);
530555
}
@@ -536,8 +561,9 @@ class NativeAudioSource {
536561

537562
skipBuffers(AL.getSourcei(source, AL.BUFFERS_PROCESSED));
538563

539-
final process = requestBuffers < STREAM_MIN_BUFFERS ? STREAM_MIN_BUFFERS - requestBuffers : 0;
540-
fillBuffers(STREAM_PROCESS_BUFFERS > process ? STREAM_PROCESS_BUFFERS : process);
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);
541567
flushBuffers();
542568

543569
if (AL.getSourcei(source, AL.SOURCE_STATE) == AL.STOPPED) {

0 commit comments

Comments
 (0)