Skip to content

Instantly share code, notes, and snippets.

@Yanrishatum
Created June 19, 2016 16:53
Show Gist options
  • Save Yanrishatum/25aebff4f2824146c76f28faf63c920c to your computer and use it in GitHub Desktop.
Save Yanrishatum/25aebff4f2824146c76f28faf63c920c to your computer and use it in GitHub Desktop.
Streaming audio fast implementation
package utils.system;
import haxe.io.BytesData;
import lime.audio.openal.AL;
import lime.utils.UInt16Array;
//import openal.AL;
import lime.utils.UInt8Array;
import ogg.Ogg;
import openfl.Assets;
import openfl.utils.ByteArray;
import utils.system.OAL.StreamingAudio;
import utils.LogUtil.*;
/**
* ...
* @author Yanrishatum
*/
class OAL
{
public static var activeSounds:Array<StreamingAudio> = new Array();
public static function createStream(path:String):StreamingAudio
{
#if ios
path = "assets/" + path;
#end
var file:OggVorbisFile = Ogg.newOggVorbisFile();
var opened:Bool = Ogg.ov_fopen(path, file) == 0;
if (!opened) throw "Failed to open stream";
//verbose("Creating audio stream");
var stream:StreamingAudio = new StreamingAudio(file);
return stream;
}
public static function update():Void
{
for (sound in activeSounds)
{
if (sound.shouldPlay)
{
if (!sound.update()) sound.play();
}
}
}
}
class StreamingAudio
{
public var audio:OggVorbisFile;
public var info:VorbisInfo;
public var mono:Bool;
private var buffers:Array<Int>;
private var dataBuffers:Array<UInt8Array>;
private var source:Int;
private static inline var BUFFER_COUNT:Int = 4;
public static inline var BUFFER_SIZE:Int = 4096 * 8;
#if HXCPP_BIG_ENDIAN
public static inline var ENDIAN:Int = 0;
#else
public static inline var ENDIAN:Int = 1;
#end
public var length:Float;
public var volume(get, set):Float;
private inline function get_volume():Float
{
return AL.getSourcef(source, AL.GAIN);
}
private inline function set_volume(v:Float):Float
{
AL.sourcef(source, AL.GAIN, v);
return v;
}
public function new(file:OggVorbisFile):Void
{
OAL.activeSounds.push(this);
audio = file;
info = Ogg.ov_info(file, -1);
length = Ogg.ov_time_total(file, -1);
//info.rate = Std.int(info.rate / info.channels); // Lol
mono = info.channels == 1;
//verbose("Info: " + info);
//verbose("Creating handles for OpenAL");
buffers = AL.genBuffers(BUFFER_COUNT);
dataBuffers = new Array();
for (i in 0...BUFFER_COUNT) dataBuffers.push(new UInt8Array(BUFFER_SIZE));
source = AL.genSource();
AL.source3f(source, AL.POSITION , 0.0, 0.0, 0.0);
AL.source3f(source, AL.VELOCITY , 0.0, 0.0, 0.0);
AL.source3f(source, AL.DIRECTION , 0.0, 0.0, 0.0);
AL.sourcef (source, AL.ROLLOFF_FACTOR , 0.0);
//AL.sourcef(source, AL.PITCH, 0.5);
AL.sourcei (source, AL.SOURCE_RELATIVE, AL.TRUE);
}
public function destroy():Void
{
OAL.activeSounds.remove(this);
AL.sourceStop(source);
AL.deleteSource(source);
AL.deleteBuffers(buffers);
Ogg.ov_clear(audio);
audio = null;
info = null;
buffers = null;
}
public var shouldPlay:Bool = false;
public var playing(get, never):Bool;
private function get_playing():Bool
{
return AL.getSourcei(source, AL.SOURCE_STATE) == AL.PLAYING;
}
public function play(restart:Bool = true):Bool
{
shouldPlay = true;
if (playing) return true;
//verbose("Trying to seek 0");
if (restart && Ogg.ov_time_seek(audio, 0) != 0) throw "Ogg seeking failed!";
//verbose("Playing");
for (i in 0...BUFFER_COUNT) if (!stream(buffers[i])) return false;
AL.sourceQueueBuffers(source, BUFFER_COUNT, buffers);
AL.sourcePlay(source);
return true;
}
public function update():Bool
{
//if (!playing) play(false);
//AL.sourcei(source, AL.LOOPING, AL.FALSE);
var processed:Int = AL.getSourcei(source, AL.BUFFERS_PROCESSED);
var active:Bool = true;
while (processed-- > 0)
{
var buf:Int = AL.sourceUnqueueBuffer(source);
//verbose("Filling buffer");
if (Ogg.ov_time_tell(audio) >= length) Ogg.ov_time_seek(audio, 0);
active = stream(buf);
AL.sourceQueueBuffers(source, 1, [buf]);
}
if (active && !playing) AL.sourcePlay(source);
return active;
}
private function stream(buf:Int):Bool
{
var dataBuffer = dataBuffers[buffers.indexOf(buf)];
var data:BytesData = dataBuffer.buffer.getData();
var size:Int = 0;
var result:Int;
while (size < BUFFER_SIZE)
{
result = Ogg.ov_read (audio, data, size, BUFFER_SIZE - size, 0, 2, 1);
//result = Ogg.ov_read(audio, data, size, BUFFER_SIZE - size, ENDIAN, OggWord.OGG_8_BIT, OggSigned.OGG_SIGNED);
if (result > 0) size += result;
else if (result < 0) throw result; // ???
else break;
}
if (size == 0) return false;
AL.bufferData(buf, mono ? AL.FORMAT_MONO16 : AL.FORMAT_STEREO16, dataBuffer, size, info.rate);
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment