1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . IO ;
4+ using NAudio . Wave ;
5+
6+ namespace Fmod5Sharp
7+ {
8+ public static class FmodImaAdPcmRebuilder
9+ {
10+ public const int SamplesPerFramePerChannel = 64 ;
11+
12+ static readonly int [ ] ADPCMTable = {
13+ 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 ,
14+ 16 , 17 , 19 , 21 , 23 , 25 , 28 , 31 ,
15+ 34 , 37 , 41 , 45 , 50 , 55 , 60 , 66 ,
16+ 73 , 80 , 88 , 97 , 107 , 118 , 130 , 143 ,
17+ 157 , 173 , 190 , 209 , 230 , 253 , 279 , 307 ,
18+ 337 , 371 , 408 , 449 , 494 , 544 , 598 , 658 ,
19+ 724 , 796 , 876 , 963 , 1060 , 1166 , 1282 , 1411 ,
20+ 1552 , 1707 , 1878 , 2066 , 2272 , 2499 , 2749 , 3024 ,
21+ 3327 , 3660 , 4026 , 4428 , 4871 , 5358 , 5894 , 6484 ,
22+ 7132 , 7845 , 8630 , 9493 , 10442 , 11487 , 12635 , 13899 ,
23+ 15289 , 16818 , 18500 , 20350 , 22385 , 24623 , 27086 , 29794 ,
24+ 32767
25+ } ;
26+
27+ private static readonly int [ ] IMA_IndexTable = {
28+ - 1 , - 1 , - 1 , - 1 , 2 , 4 , 6 , 8 ,
29+ - 1 , - 1 , - 1 , - 1 , 2 , 4 , 6 , 8 ,
30+ } ;
31+
32+ private static void ExpandNibble ( MemoryStream stream , long byteOffset , int nibbleShift , ref int hist , ref int stepIndex )
33+ {
34+ stream . Seek ( byteOffset , SeekOrigin . Begin ) ;
35+ var sampleNibble = ( stream . ReadByte ( ) >> nibbleShift ) & 0xf ;
36+ var sampleDecoded = hist ;
37+ var step = ADPCMTable [ stepIndex ] ;
38+
39+ var delta = step >> 3 ;
40+ if ( ( sampleNibble & 1 ) != 0 ) delta += step >> 2 ;
41+ if ( ( sampleNibble & 2 ) != 0 ) delta += step >> 1 ;
42+ if ( ( sampleNibble & 4 ) != 0 ) delta += step ;
43+ if ( ( sampleNibble & 8 ) != 0 ) delta = - delta ;
44+ sampleDecoded += delta ;
45+
46+ hist = Math . Clamp ( sampleDecoded , short . MinValue , short . MaxValue ) ;
47+ stepIndex += IMA_IndexTable [ sampleNibble ] ;
48+ stepIndex = Math . Clamp ( stepIndex , 0 , 88 ) ;
49+ }
50+
51+ private static short [ ] GetPcm ( FmodSample sample )
52+ {
53+ var blockSamples = 0x40 ;
54+ var numChannels = ( int ) sample . Metadata . Channels ;
55+
56+ using var stream = new MemoryStream ( sample . SampleBytes ) ;
57+ using var reader = new BinaryReader ( stream ) ;
58+
59+ short [ ] ret = new short [ sample . Metadata . SampleCount * 2 ] ;
60+ var sampleIndex = 0 ;
61+
62+ for ( var channel = 0 ; channel < numChannels ; channel ++ )
63+ {
64+ sampleIndex = channel ;
65+
66+ var numFrames = ( int ) sample . Metadata . SampleCount / SamplesPerFramePerChannel ;
67+
68+ for ( var frameNum = 0 ; frameNum < numFrames ; frameNum ++ )
69+ {
70+ var frameOffset = 0x24 * numChannels * frameNum ;
71+
72+ //Read header
73+ var headerIndex = frameOffset + 4 * channel ;
74+
75+ stream . Seek ( headerIndex , SeekOrigin . Begin ) ;
76+ int hist = reader . ReadInt16 ( ) ;
77+ stream . Seek ( headerIndex + 2 , SeekOrigin . Begin ) ;
78+ int stepIndex = reader . ReadByte ( ) ;
79+
80+ stepIndex = Math . Clamp ( stepIndex , 0 , 88 ) ;
81+ ret [ sampleIndex ] = ( short ) hist ;
82+ sampleIndex += numChannels ;
83+
84+ for ( var sampleNum = 1 ; sampleNum <= SamplesPerFramePerChannel ; sampleNum ++ )
85+ {
86+ // var byteOffset = relativePos + 4 * numChannels + 2 * channel + (i - 1) / 4 * 2 * numChannels + ((i - 1) % 4) / 2;
87+ var byteOffset = frameOffset + 4 * 2 + 4 * ( channel % 2 ) + 4 * 2 * ( ( sampleNum - 1 ) / 8 ) + ( ( sampleNum - 1 ) % 8 ) / 2 ;
88+ if ( numChannels == 0 )
89+ byteOffset = frameOffset + 4 + ( sampleNum - 1 ) / 2 ;
90+
91+ var nibbleShift = ( ( sampleNum - 1 ) & 1 ) != 0 ? 4 : 0 ;
92+
93+ if ( sampleNum < blockSamples )
94+ {
95+ ExpandNibble ( stream , byteOffset , nibbleShift , ref hist , ref stepIndex ) ;
96+ ret [ sampleIndex ] = ( ( short ) hist ) ;
97+ sampleIndex += numChannels ;
98+ }
99+ else
100+ {
101+ // relativePos += 0x24 * numChannels;
102+ }
103+ }
104+ }
105+ }
106+
107+ return ret ;
108+ }
109+
110+ public static byte [ ] Rebuild ( FmodSample sample )
111+ {
112+ var numChannels = sample . Metadata . IsStereo ? 2 : 1 ;
113+ var format = WaveFormat . CreateCustomFormat (
114+ WaveFormatEncoding . Pcm ,
115+ sample . Metadata . Frequency ,
116+ numChannels ,
117+ sample . Metadata . Frequency * numChannels * 2 ,
118+ numChannels * 2 ,
119+ 16
120+ ) ;
121+ using var stream = new MemoryStream ( ) ;
122+ using var writer = new WaveFileWriter ( stream , format ) ;
123+
124+ var pcmShorts = GetPcm ( sample ) ;
125+
126+ writer . WriteSamples ( pcmShorts , 0 , pcmShorts . Length ) ;
127+
128+ return stream . ToArray ( ) ;
129+ }
130+ }
131+ }
0 commit comments