Explorar el Código

Add MP3 support to Hashlink. (#514)

Add MP3 support to Hashlink target
Pavel Alexandrov hace 6 años
padre
commit
289b2a1369
Se han modificado 3 ficheros con 94 adiciones y 7 borrados
  1. 2 0
      hxd/res/Config.hx
  2. 1 7
      hxd/res/Sound.hx
  3. 91 0
      hxd/snd/Mp3Data.hx

+ 2 - 0
hxd/res/Config.hx

@@ -67,7 +67,9 @@ class Config {
 			Unknown;
 		switch( pf ) {
 		case HL:
+			#if !heaps_enable_hl_mp3
 			ignoredExtensions.set("mp3", true);
+			#end
 		default:
 			ignoredExtensions.set("ogg", true);
 		}

+ 1 - 7
hxd/res/Sound.hx

@@ -14,14 +14,8 @@ class Sound extends Resource {
 
 	public static function supportedFormat( fmt : SoundFormat ) {
 		return switch( fmt ) {
-		case Wav:
+		case Wav, Mp3:
 			return true;
-		case Mp3:
-			#if (flash || js)
-			return true;
-			#else
-			return false;
-			#end
 		case OggVorbis:
 			#if (hl || stb_ogg_sound)
 			return true;

+ 91 - 0
hxd/snd/Mp3Data.hx

@@ -1,5 +1,11 @@
 package hxd.snd;
 
+#if hl
+
+private typedef Mp3File = hl.Abstract<"fmt_mp3">;
+
+#end
+
 class Mp3Data extends Data {
 
 	#if flash
@@ -7,6 +13,15 @@ class Mp3Data extends Data {
 	#elseif js
 	var buffer : haxe.io.Bytes;
 	var onEnd : Void -> Void;
+	#elseif hl
+	var bytes : haxe.io.Bytes;
+	var frameOffsets:Array<Int>;
+	var frameSamples:Array<Int>; // Sample offset of a frame.
+	var reader : Mp3File;
+	var frame : haxe.io.Bytes;
+	var currentFrame : Int;
+	var currentSample : Int;
+	var frameEnd : Int;
 	#end
 
 	public function new( bytes : haxe.io.Bytes ) {
@@ -56,6 +71,36 @@ class Mp3Data extends Data {
 		samples = Math.ceil(samples * decodedRate / samplingRate);
 		samplingRate = decodedRate;
 
+		#elseif hl
+
+		this.bytes = bytes;
+		// Re-reading MP3 to find offsets to each frame in file for seeking.
+		frameOffsets = new Array();
+		frameSamples = new Array();
+		var byteInput = new haxe.io.BytesInput(bytes);
+		var reader = new format.mp3.Reader(byteInput);
+		var ft;
+		var totalSamples : Int = 0;
+		while ( (ft = reader.seekFrame()) == FT_MP3 ) {
+			var header = reader.readFrameHeader();
+			if ( header != null && !format.mp3.Tools.isInvalidFrameHeader(header) ) {
+				// TODO: Layer I support. Layer I frames contain only 384 frames.
+				var len = format.mp3.Tools.getSampleDataSizeHdr(header);
+				if ( byteInput.length - byteInput.position >= len ) {
+					frameOffsets.push(byteInput.position - 4);
+					frameSamples.push(totalSamples);
+					totalSamples += format.mp3.Tools.getSampleCountHdr(header);
+					byteInput.position += len;
+				}
+			}
+		}
+
+		this.frame = haxe.io.Bytes.alloc(1152*channels*4); // 2 channels, F32.
+		this.currentSample = -1;
+		this.frameEnd = -1;
+		this.currentFrame = -1;
+		this.reader = mp3_open(bytes, bytes.length);
+
 		#end
 	}
 
@@ -119,9 +164,55 @@ class Mp3Data extends Data {
 		} else {
 			out.blit(outPos, buffer, sampleStart * 4 * channels, sampleCount * 4 * channels);
 		}
+		#elseif hl
+		if ( currentSample != sampleStart ) {
+			var i = frameSamples.length - 1;
+			while ( i >= 0 ) {
+				if ( frameSamples[i] <= sampleStart ) {
+					if ( this.currentFrame != i ) {
+						seekFrame(i);
+						this.currentSample = sampleStart;
+					}
+					break;
+				}
+				i--;
+			}
+		}
+		var targetSample = sampleStart + sampleCount;
+		while (frameEnd <= targetSample) {
+			var frameStart = frameSamples[currentFrame];
+			var offset = (frameEnd - currentSample) * 4 * channels;
+			out.blit(outPos, frame, (currentSample - frameStart) * 4 * channels, offset);
+			currentSample = frameEnd;
+			outPos += offset;
+			seekFrame(currentFrame + 1);
+		}
+		if ( currentSample < targetSample ) {
+			var frameStart = frameSamples[currentFrame];
+			out.blit(outPos, frame, (currentSample - frameStart) * 4 * channels, (targetSample - currentSample) * channels * 4);
+			currentSample = targetSample;
+		}
 		#else
 		throw "MP3 decoding is not available for this platform";
 		#end
 	}
 
+	#if hl
+
+	function seekFrame(to:Int) {
+		currentFrame = to;
+		var samples : Int = mp3_decode_frame(reader, bytes, bytes.length, frameOffsets[to], frame, frame.length, 0);
+		frameEnd = frameSamples[to] + samples;
+	}
+	
+	@:hlNative("fmt", "mp3_open") static function mp3_open( bytes : hl.Bytes, size : Int ) : Mp3File {
+		return null;
+	}
+
+	@:hlNative("fmt", "mp3_decode_frame") static function mp3_decode_frame( o : Mp3File, bytes : hl.Bytes, size : Int, position : Int, output : hl.Bytes, outputSize : Int, offset : Int ) : Int {
+		return 0;
+	}
+
+	#end
+
 }