Skip to content

Commit cc220bd

Browse files
committed
feat: Support analyzing streamed audio
1 parent 02bada1 commit cc220bd

File tree

7 files changed

+147
-24
lines changed

7 files changed

+147
-24
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"lime.projectFile": "example/Project.xml"
3+
}

example/Project.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<project xmlns="http://lime.software/project/1.0.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3-
xsi:schemaLocation="http://lime.software/project/1.0.2 http://lime.software/xsd/project-1.0.2.xsd">
2+
<project xmlns="http://lime.openfl.org/project/1.0.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://lime.openfl.org/project/1.0.4 http://lime.openfl.org/xsd/project-1.0.4.xsd">
44

55
<!-- _________________________ Application Settings _________________________ -->
66

@@ -16,7 +16,7 @@
1616
<!-- ____________________________ Window Settings ___________________________ -->
1717

1818
<!--These window settings apply to all targets-->
19-
<window width="1280" height="720" fps="" background="#000000" hardware="true" vsync="false" />
19+
<window width="1280" height="720" fps="60" background="#000000" hardware="true" vsync="false" />
2020

2121
<!--HTML5-specific-->
2222
<window if="html5" resizable="true" />
@@ -37,7 +37,7 @@
3737

3838
<haxelib name="flixel" />
3939
<haxelib name="grig.audio" />
40-
<haxelib name="funkin.vis" />
40+
<haxelib name="funkin.vis" path="../" />
4141
<!-- <haxeflag name="macro" value="instrument.Instrumentation.profiling(['funkin.vis.dsp.FFT'])" /> -->
4242
<!-- <haxedef name="profiler-console-hierarchy-reporter" /> -->
4343
<!--In case you want to use the addons package-->

example/source/PlayState.hx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@ import haxe.io.BytesInput;
77
import haxe.io.Input;
88
import haxe.io.UInt16Array;
99
import lime.media.AudioSource;
10-
import lime.media.vorbis.VorbisFile;
1110
import lime.utils.Int16Array;
1211
import openfl.utils.Assets;
1312

13+
#if STREAM
14+
import lime.media.vorbis.VorbisFile;
15+
import lime.media.AudioBuffer;
16+
import openfl.media.Sound;
17+
#end
18+
1419
using StringTools;
1520

1621
class PlayState extends FlxState
@@ -27,10 +32,17 @@ class PlayState extends FlxState
2732
super.create();
2833

2934
// musicList = fillMusicList("assets/music/musicList.txt");
35+
36+
#if STREAM
37+
var vorbis = VorbisFile.fromFile("assets/music/catStuck.ogg");
38+
var buffer = AudioBuffer.fromVorbisFile(vorbis);
39+
FlxG.sound.playMusic(Sound.fromAudioBuffer(buffer));
40+
#else
3041
FlxG.sound.playMusic("assets/music/catStuck.ogg");
42+
#end
3143

3244
@:privateAccess
33-
musicSrc = cast FlxG.sound.music._channel.__source;
45+
musicSrc = cast #if (openfl < "9.3.2") FlxG.sound.music._channel.__source #else FlxG.sound.music._channel.__audioSource #end;
3446

3547
data = cast musicSrc.buffer.data;
3648

src/funkin/vis/AudioBuffer.hx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ class AudioBuffer
66
{
77
public var data(default, null):UInt16Array;
88
public var sampleRate(default, null):Float;
9+
public var length(default, null):Int;
10+
public var bitsPerSample(default, null):Int;
11+
public var channels(default, null):Int;
912

10-
public function new(data:UInt16Array, sampleRate:Float)
13+
public function new(data:UInt16Array, sampleRate:Float, length:Int, bitsPerSample:Int, channels:Int)
1114
{
1215
this.data = data;
1316
this.sampleRate = sampleRate;
17+
this.length = length;
18+
this.bitsPerSample = bitsPerSample;
19+
this.channels = channels;
1420
}
1521
}

src/funkin/vis/AudioClip.hx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ interface AudioClip
88
public var audioBuffer(default, null):AudioBuffer;
99
public var currentFrame(get, never):Int;
1010
public var source:Dynamic;
11+
public var streamed:Bool;
1112
}

src/funkin/vis/audioclip/frontends/LimeAudioClip.hx

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package funkin.vis.audioclip.frontends;
22

3+
import haxe.Int64;
34
import flixel.FlxG;
45
import flixel.math.FlxMath;
56
import funkin.vis.AudioBuffer;
@@ -20,32 +21,61 @@ class LimeAudioClip implements funkin.vis.AudioClip
2021
public var audioBuffer(default, null):AudioBuffer;
2122
public var currentFrame(get, never):Int;
2223
public var source:Dynamic;
24+
public var streamed:Bool;
2325

2426
public function new(audioSource:AudioSource)
2527
{
26-
var data:lime.utils.UInt16Array = cast audioSource.buffer.data;
28+
var limeBuffer = audioSource.buffer;
29+
var data:lime.utils.UInt16Array = cast limeBuffer.data;
2730

2831
#if web
29-
var sampleRate:Float = audioSource.buffer.src._sounds[0]._node.context.sampleRate;
32+
streamed = false;
33+
34+
var sampleRate:Float = limeBuffer.src._sounds[0]._node.context.sampleRate;
35+
var length:Int = audioSource.length;
36+
var bitsPerSample:Int = 32;
37+
var channels:Int = 2;
3038
#else
31-
var sampleRate = audioSource.buffer.sampleRate;
39+
var sampleRate:Float = 0;
40+
var length:Int = 0;
41+
var bitsPerSample:Int = 0;
42+
var channels:Int = 0;
43+
44+
#if lime_vorbis
45+
// If we have a ref to a VorbisFile it should be safe to assume
46+
// this is a streamed sound!
47+
@:privateAccess
48+
if (limeBuffer.__srcVorbisFile != null)
49+
{
50+
streamed = true;
51+
52+
var vorbisFile = limeBuffer.__srcVorbisFile;
53+
var vorbisInfo = vorbisFile.info();
54+
55+
sampleRate = vorbisInfo.rate;
56+
bitsPerSample = 16;
57+
length = Std.int(Int64.toInt(vorbisFile.pcmTotal()) * vorbisInfo.channels * (bitsPerSample / 8));
58+
channels = vorbisInfo.channels;
59+
}
60+
else
61+
#end
62+
{
63+
streamed = false;
64+
65+
sampleRate = limeBuffer.sampleRate;
66+
bitsPerSample = limeBuffer.bitsPerSample;
67+
length = limeBuffer.data.length;
68+
channels = limeBuffer.channels;
69+
}
3270
#end
3371

34-
this.audioBuffer = new AudioBuffer(data, sampleRate);
72+
this.audioBuffer = new AudioBuffer(data, sampleRate, length, bitsPerSample, channels);
3573
this.source = audioSource.buffer.src;
3674
}
3775

3876
private function get_currentFrame():Int
3977
{
40-
var dataLength:Int = 0;
41-
42-
#if web
43-
dataLength = source.length;
44-
#else
45-
dataLength = audioBuffer.data.length;
46-
#end
47-
48-
var value = Std.int(FlxMath.remapToRange(FlxG.sound.music.time, 0, FlxG.sound.music.length, 0, dataLength));
78+
var value = Std.int(FlxMath.remapToRange(FlxG.sound.music.time, 0, FlxG.sound.music.length, 0, audioBuffer.length));
4979

5080
if (value < 0)
5181
return -1;

src/funkin/vis/dsp/SpectralAnalyzer.hx

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package funkin.vis.dsp;
22

3+
import haxe.io.Bytes;
4+
import lime.utils.UInt8Array;
35
import flixel.FlxG;
46
import flixel.math.FlxMath;
57
import funkin.vis._internal.html5.AnalyzerNode;
@@ -8,6 +10,10 @@ import grig.audio.FFT;
810
import grig.audio.FFTVisualization;
911
import lime.media.AudioSource;
1012

13+
#if lime_vorbis
14+
import lime.media.vorbis.VorbisFile;
15+
#end
16+
1117
using grig.audio.lime.UInt8ArrayTools;
1218

1319
typedef Bar =
@@ -58,6 +64,9 @@ class SpectralAnalyzer
5864
#end
5965
private var _logGraphCache:Array<Int> = [];
6066
private var _mixedCache:Array<Float> = [];
67+
#if lime_vorbis
68+
private var vorbisBuffer:UInt8Array;
69+
#end
6170

6271
private function freqToBin(freq:Float, mathType:MathType = Round):Int
6372
{
@@ -193,8 +202,8 @@ class SpectralAnalyzer
193202

194203
return levels;
195204
#else
196-
var numOctets = Std.int(audioSource.buffer.bitsPerSample / 8);
197-
var wantedLength = fftN * numOctets * audioSource.buffer.channels;
205+
var numOctets = Std.int(audioClip.audioBuffer.bitsPerSample / 8);
206+
var wantedLength = fftN * numOctets * audioClip.audioBuffer.channels;
198207
var startFrame = audioClip.currentFrame;
199208

200209
if (startFrame < 0)
@@ -203,9 +212,34 @@ class SpectralAnalyzer
203212
}
204213

205214
startFrame -= startFrame % numOctets;
206-
var segment = audioSource.buffer.data.subarray(startFrame, min(startFrame + wantedLength, audioSource.buffer.data.length));
207215

208-
getSignal(segment, audioSource.buffer.bitsPerSample); // Sets _buffer
216+
var endFrame:Int = min(startFrame + wantedLength, audioClip.audioBuffer.length);
217+
218+
#if lime_vorbis
219+
if (audioClip.streamed)
220+
{
221+
@:privateAccess
222+
var vorbisFile = audioSource.buffer.__srcVorbisFile;
223+
224+
// reading from VorbisFile will automatically move the position
225+
// which causes issues with playback, so we keep old time to go back to it
226+
var prevPos = vorbisFile.pcmTell();
227+
228+
vorbisFile.pcmSeek(Std.int(startFrame / (numOctets + audioClip.audioBuffer.channels)));
229+
230+
// calling this updates the vorbisBuffer array
231+
readVorbisFileBuffer(vorbisFile, wantedLength);
232+
233+
vorbisFile.pcmSeek(prevPos);
234+
235+
getSignal(vorbisBuffer, audioClip.audioBuffer.bitsPerSample);
236+
}
237+
else
238+
#end
239+
{
240+
var segment:UInt8Array = audioSource.buffer.data.subarray(startFrame, endFrame);
241+
getSignal(segment, audioClip.audioBuffer.bitsPerSample);
242+
}
209243

210244
if (audioSource.buffer.channels > 1) {
211245
var wantedArrayLength = Std.int(_buffer.length / audioSource.buffer.channels);
@@ -290,6 +324,43 @@ class SpectralAnalyzer
290324
return _buffer;
291325
}
292326

327+
#if lime_vorbis
328+
// Pretty much copied from
329+
// https://github.com/openfl/lime/blob/develop/src/lime/_internal/backend/native/NativeAudioSource.hx#L212
330+
function readVorbisFileBuffer(vorbisFile:VorbisFile, length:Int):UInt8Array
331+
{
332+
if (vorbisBuffer == null || vorbisBuffer.length != length)
333+
vorbisBuffer = new UInt8Array(length);
334+
335+
var read:Int = 0;
336+
var total:Int = 0;
337+
var readMax:Int = 4096;
338+
339+
while (total < length)
340+
{
341+
readMax = 4096;
342+
343+
if (readMax > length - total)
344+
{
345+
readMax = length - total;
346+
}
347+
348+
read = vorbisFile.read(vorbisBuffer.buffer, total, readMax);
349+
350+
if (read > 0)
351+
{
352+
total += read;
353+
}
354+
else
355+
{
356+
break;
357+
}
358+
}
359+
360+
return vorbisBuffer;
361+
}
362+
#end
363+
293364
@:generic
294365
static inline function clamp<T:Float>(val:T, min:T, max:T):T
295366
{

0 commit comments

Comments
 (0)