Skip to content

Commit

Permalink
Partial support for IMA ADPCM.
Browse files Browse the repository at this point in the history
  • Loading branch information
datacompboy committed Aug 12, 2011
1 parent e76ad91 commit d10a8ad
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 13 deletions.
2 changes: 1 addition & 1 deletion WavPlayer.hx
Expand Up @@ -303,7 +303,7 @@ class WavPlayerGui_Full extends WavPlayerGui {


// Main user interface: play / stop buttons & ExternalInterface // Main user interface: play / stop buttons & ExternalInterface
class WavPlayer { class WavPlayer {
static var Version = "1.7.4"; static var Version = "1.8.0beta1";
static var player : Player; static var player : Player;
static var state : String = PlayerEvent.STOPPED; static var state : String = PlayerEvent.STOPPED;
static var handlers : List<JsEventHandler>; static var handlers : List<JsEventHandler>;
Expand Down
5 changes: 3 additions & 2 deletions debug.html
Expand Up @@ -44,7 +44,8 @@
<body bgcolor="#dddddd" onload="init()"> <body bgcolor="#dddddd" onload="init()">
<a href="javascript:doPlay()">doPlay()</a> <a href="javascript:doPlay()">doPlay()</a>
<a href="javascript:doPlay('test.gsm')">doPlay('test.gsm')</a> <a href="javascript:doPlay('test.gsm')">doPlay('test.gsm')</a>
<a href="javascript:doPlay('test-vf-44100.au')">doPlay('test-vf-44100.au')</a> <a href="javascript:doPlay('test.wav')">doPlay('test.wav')</a>
<a href="javascript:doPlay('test2.wav')">doPlay('test2.wav')</a>
<a href="javascript:doPlay('test.au')">doPlay('test.au')</a> <a href="javascript:doPlay('test.au')">doPlay('test.au')</a>
<a href="javascript:doPlay('hl-78003330781-5627.au')">doPlay('hl-78003330781-5627.au')</a> <a href="javascript:doPlay('hl-78003330781-5627.au')">doPlay('hl-78003330781-5627.au')</a>
<a href="javascript:doStop()">doStop()</a><br/> <a href="javascript:doStop()">doStop()</a><br/>
Expand Down Expand Up @@ -77,7 +78,7 @@
<embed src="wavplayer-debug.swf?gui=full&h=40&w=400&sound=test.au&4" <embed src="wavplayer-debug.swf?gui=full&h=40&w=400&sound=test.au&4"
bgcolor="#ffffff" bgcolor="#ffffff"
width="1200" width="1200"
height="400" height="4000"
name="haxe" name="haxe"
quality="high" quality="high"
align="middle" align="middle"
Expand Down
2 changes: 1 addition & 1 deletion fmt/Decoder.hx
Expand Up @@ -23,5 +23,5 @@ interface Decoder {
// Number of PCM samples in one input chunk // Number of PCM samples in one input chunk
var sampleLength : Int; var sampleLength : Int;
// Decode one input chunk to PCM samples // Decode one input chunk to PCM samples
function decode( InBuf : haxe.io.BytesData, InOff: Int, OutBuf : Array<Float>, OutOff: Int ) : Int; function decode( InBuf : haxe.io.BytesData, InOff: Int, Chan: Int, OutBuf : Array<Float>, OutOff: Int ) : Int;
} }
2 changes: 1 addition & 1 deletion fmt/DecoderG711a.hx
Expand Up @@ -32,7 +32,7 @@ class DecoderG711a implements fmt.Decoder {
inverted = inv; inverted = inv;
generate(); generate();
} }
public function decode( InBuf : haxe.io.BytesData, InOff: Int, OutBuf : Array<Float>, OutOff : Int) : Int { public function decode( InBuf : haxe.io.BytesData, InOff: Int, Chan: Int, OutBuf : Array<Float>, OutOff : Int) : Int {
if (inverted) { if (inverted) {
OutBuf[OutOff] = aLaw[Inv[InBuf[InOff]]]; OutBuf[OutOff] = aLaw[Inv[InBuf[InOff]]];
} else { } else {
Expand Down
2 changes: 1 addition & 1 deletion fmt/DecoderG711u.hx
Expand Up @@ -32,7 +32,7 @@ class DecoderG711u implements fmt.Decoder {
inverted = inv; inverted = inv;
generate(); generate();
} }
public function decode( InBuf : haxe.io.BytesData, InOff: Int, OutBuf : Array<Float>, OutOff: Int) : Int { public function decode( InBuf : haxe.io.BytesData, InOff: Int, Chan: Int, OutBuf : Array<Float>, OutOff: Int) : Int {
if (inverted) { if (inverted) {
OutBuf[OutOff] = ulaw[Inv[InBuf[InOff]]]; OutBuf[OutOff] = ulaw[Inv[InBuf[InOff]]];
} else { } else {
Expand Down
2 changes: 1 addition & 1 deletion fmt/DecoderGSM.hx
Expand Up @@ -39,7 +39,7 @@ class DecoderGSM implements fmt.Decoder {
throw "Unsupported BPS"; throw "Unsupported BPS";
decoder = new GSMDecoder(); decoder = new GSMDecoder();
} }
public function decode( InBuf : haxe.io.BytesData, InOff: Int, OutBuf : Array<Float>, OutOff: Int) : Int { public function decode( InBuf : haxe.io.BytesData, InOff: Int, Chan: Int, OutBuf : Array<Float>, OutOff: Int) : Int {
decoder.decode( InBuf, InOff, OutBuf, OutOff, wavmode ); decoder.decode( InBuf, InOff, OutBuf, OutOff, wavmode );
return wavmode ? 320 : 160; return wavmode ? 320 : 160;
} }
Expand Down
122 changes: 122 additions & 0 deletions fmt/DecoderIMAADPCM.hx
@@ -0,0 +1,122 @@
//
// WAV/AU Flash player with resampler
//
// Copyright (c) 2011, Anton Fedorov <datacompboy@call2ru.com>
//
/* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*/
package fmt;

// IMA ADPCM decoder for MS/4bit
class IMAADPCM {
var predictor : Int;
var index : Int;
var step : Int;
var proceed : Int;
var resync : Int;

static var ima_index_table : Array<Int> = [
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
];
static var ima_step_table : Array<Int> = [ // 89 values 0-88
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
];

var calcs: Int;
public function new(samples: Int) {
proceed = 0;
calcs = 0;
resync = Std.int((samples+7)/8);
}

static var show: Int = 0;
function calc(nibble: Int): Float {
var diff: Int;
calcs++;
step = ima_step_table[index];
index += ima_index_table[nibble];
if (index < 0) index = 0;
if (index > 88) index = 88;
//diff = Std.int(((nibble&7)+0.5)*step/4.0);
diff = step >> 3;
if (nibble&4!=0) diff += step;
if (nibble&2!=0) diff += step>>1;
if (nibble&1!=0) diff += step>>2;
if (nibble&8!=0) {
predictor -= diff;
} else {
predictor += diff;
}
if (predictor < -32768) predictor = -32768;
if (predictor > 32767) predictor = 32767;
if (predictor != 0) {
if(show++<5) {
trace("Nibble="+nibble+" step="+step+" index="+index+" diff="+diff);
trace("Non-null predictor at "+calcs+" = "+predictor);
}
}
return predictor / 32767.0;
}

public function decode( InBuf : haxe.io.BytesData, Off: Int, OutBuf: Array<Float>, OutOff: Int) : Int {
if ((proceed++) % resync == 0) {
// Read initial pack
predictor = InBuf[Off] * 256 + InBuf[Off+1];
if (predictor > 32767) predictor = 65536-predictor;
index = InBuf[Off+2];
OutBuf[OutOff] = predictor/32767.0;
return 1;
} else {
for(i in 0...4) {
var n: Int = InBuf[Off++];
OutBuf[OutOff++] = calc(n&0x0F);
OutBuf[OutOff++] = calc(n>>4);
}
return 8;
}
}
}
class DecoderIMAADPCM implements fmt.Decoder {
public var sampleSize : Int;
public var sampleLength : Int;
var channels : Array<IMAADPCM>;

public function new(bps : Int, chans: Int, ?align: Int, ?samplesPerBlock: Int) {
var i: Int;
if (bps != 4) {
trace("Unsupported BPS");
throw "Unsupported BPS";
}
sampleSize = 4;
sampleLength = 8;
if ( (samplesPerBlock-1)/8+1 != align/4) {
trace("Unsupported packing");
throw "Unsupported packing";
}
channels = new Array<IMAADPCM>();
for (i in 0...chans) {
channels.push( new IMAADPCM(samplesPerBlock) );
}
}

public function decode( InBuf : haxe.io.BytesData, Off: Int, Chan: Int, OutBuf: Array<Float>, OutOff: Int) : Int {
return channels[Chan].decode(InBuf, Off, OutBuf, OutOff);
}
}
2 changes: 1 addition & 1 deletion fmt/DecoderPCM.hx
Expand Up @@ -28,7 +28,7 @@ class DecoderPCM implements fmt.Decoder {
shift = 1 << bps; shift = 1 << bps;
sampleLength = 1; sampleLength = 1;
} }
public function decode( InBuf : haxe.io.BytesData, Off: Int, OutBuf: Array<Float>, OutOff: Int) : Int { public function decode( InBuf : haxe.io.BytesData, Off: Int, Chan: Int, OutBuf: Array<Float>, OutOff: Int) : Int {
var Sample: Int = 0; var Sample: Int = 0;
switch (sampleSize) { switch (sampleSize) {
case 1: Sample = InBuf[Off]; case 1: Sample = InBuf[Off];
Expand Down
2 changes: 1 addition & 1 deletion fmt/File.hx
Expand Up @@ -123,7 +123,7 @@ class File implements IFile {
var chk = 0; var chk = 0;
while(SoundBuffer[0].length < samples && bufsize - i >= chunkSize) { while(SoundBuffer[0].length < samples && bufsize - i >= chunkSize) {
for(j in 0...channels) { for(j in 0...channels) {
sndDecoder.decode(Buffer, i, SoundBuffer[j], SoundBuffer[j].length); sndDecoder.decode(Buffer, i, j, SoundBuffer[j], SoundBuffer[j].length);
i += sndDecoder.sampleSize; i += sndDecoder.sampleSize;
} }
i += align; i += align;
Expand Down
11 changes: 7 additions & 4 deletions fmt/FileWav.hx
Expand Up @@ -76,7 +76,7 @@ class FileWav extends fmt.File {
dataOff = i; dataOff = i;
} }
case 1: // Read fmt block case 1: // Read fmt block
if (i-dataOff < 24) { if (i-dataOff < 24 || i-dataOff < dataSize+8) {
if (bufsize-i < 4) break; if (bufsize-i < 4) break;
var W1 = Buffer[i+1]*256+Buffer[i]; var W1 = Buffer[i+1]*256+Buffer[i];
var W2 = Buffer[i+3]*256+Buffer[i+2]; var W2 = Buffer[i+3]*256+Buffer[i+2];
Expand All @@ -102,9 +102,11 @@ class FileWav extends fmt.File {
bps = W2; bps = W2;
align = W1; align = W1;
trace("align = "+align+"; bps="+bps); trace("align = "+align+"; bps="+bps);
case 24:
trace("Appendix W1="+W1+", W2="+W2+", DW="+DW);
} }
i += 4; i += 4;
if (i-dataOff == 24) { if (i-dataOff == dataSize+8) {
if (channels < 1 || channels > 2) { if (channels < 1 || channels > 2) {
trace("Wrong number of channels: "+channels); trace("Wrong number of channels: "+channels);
Readed = -1; Readed = -1;
Expand All @@ -129,8 +131,9 @@ class FileWav extends fmt.File {
sndDecoder = new DecoderG711u(bps, false); sndDecoder = new DecoderG711u(bps, false);
case 17: case 17:
trace("File in IMA ADPCM"); trace("File in IMA ADPCM");
Readed = -1; return; sndDecoder = new DecoderIMAADPCM(bps, channels, align, W2);
//sndDecoder = new DecoderIMAADPCM(bps); align = 0; // hack :(
//Readed = -1; return;
case 20: case 20:
trace("File in G.723 ADPCM"); trace("File in G.723 ADPCM");
Readed = -1; return; Readed = -1; return;
Expand Down
Binary file modified wavplayer-debug.swf
Binary file not shown.
Binary file modified wavplayer.swf
Binary file not shown.

0 comments on commit d10a8ad

Please sign in to comment.