2 using System.Collections.Generic;
4 using System.Runtime.InteropServices;
6 using System.Diagnostics;
7 using System.Threading;
12 public unsafe class Cxa : SoundDecoder //, IDisposable
14 //static byte[] FOURCC = Encoding.ASCII.GetBytes("1DWK"); // KWD1 (little endian)
16 public CWin32.WAVEFORMATEX waveformatex;
18 private string filename;
19 private byte[] srcBuf = null;
20 private short[] pcmbuf = null;
22 //private int nHandle = -1;
23 private bjxa.Decoder bjxa = new bjxa.Decoder();
24 private bjxa.Format format = null;
25 private FileStream fs;
27 public override int Open(string filename)
29 this.filename = filename;
31 #region [ Reading XA headers, then store it ]
32 fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // Need to set FileShare flag, to avoid locking after Closer()
33 format = bjxa.ReadHeader(fs);
34 //string xaid = Encoding.ASCII.GetString( xah.id );
36 #region [ Debug info ]
37 //Debug.WriteLine( "**XAHEADER**" );
38 //Debug.WriteLine( "id= " + xaheader.id.ToString( "X8" ) );
39 //Debug.WriteLine( "nDataLen= " + xaheader.nDataLen.ToString( "X8" ) );
40 //Debug.WriteLine( "nSamples= " + xaheader.nSamples.ToString( "X8" ) );
41 //Debug.WriteLine( "nSamplesPerSec= " + xaheader.nSamplesPerSec.ToString( "X4" ) );
42 //Debug.WriteLine( "nBits= " + xaheader.nBits.ToString( "X2" ) );
43 //Debug.WriteLine( "nChannels= " + xaheader.nChannels.ToString( "X2" ) );
44 //Debug.WriteLine( "nLoopPtr= " + xaheader.nLoopPtr.ToString( "X8" ) );
45 //Debug.WriteLine( "befL[0]= " + xaheader.befL[ 0 ].ToString( "X4" ) );
46 //Debug.WriteLine( "befL[1]= " + xaheader.befL[ 1 ].ToString( "X4" ) );
47 //Debug.WriteLine( "befR[0]= " + xaheader.befR[ 0 ].ToString( "X4" ) );
48 //Debug.WriteLine( "befR[1]= " + xaheader.befR[ 1 ].ToString( "X4" ) );
53 #region [ Getting WAVEFORMEX info ]
54 waveformatex = new CWin32.WAVEFORMATEX();
56 waveformatex.wFormatTag = (ushort)format.WaveFormatPcm;
57 waveformatex.nChannels = (ushort)format.Channels;
58 waveformatex.nSamplesPerSec = format.SamplesRate;
59 waveformatex.nAvgBytesPerSec = format.WaveByteRate;
60 waveformatex.nBlockAlign = (ushort)format.WaveBlockAlign;
61 waveformatex.wBitsPerSample = (ushort)format.SampleBits;
62 waveformatex.cbSize = 0;
64 #region [ Debug info ]
65 //Debug.WriteLine( "**WAVEFORMATEX**" );
66 //Debug.WriteLine( "wFormatTag= " + waveformatex.wFormatTag.ToString( "X4" ) );
67 //Debug.WriteLine( "nChannels = " + waveformatex.nChannels.ToString( "X4" ) );
68 //Debug.WriteLine( "nSamplesPerSec= " + waveformatex.nSamplesPerSec.ToString( "X8" ) );
69 //Debug.WriteLine( "nAvgBytesPerSec= " + waveformatex.nAvgBytesPerSec.ToString( "X8" ) );
70 //Debug.WriteLine( "nBlockAlign= " + waveformatex.nBlockAlign.ToString( "X4" ) );
71 //Debug.WriteLine( "wBitsPerSample= " + waveformatex.wBitsPerSample.ToString( "X4" ) );
72 //Debug.WriteLine( "cbSize= " + waveformatex.cbSize.ToString( "X4" ) );
76 return 0x7FFFFFFF; // Anyway, return non-zero value
78 public override int GetFormat(int nHandle, ref CWin32.WAVEFORMATEX wfx)
80 #region [ Copying WAVEFORMATEX structure data ]
81 wfx.nAvgBytesPerSec = waveformatex.nAvgBytesPerSec;
82 wfx.wBitsPerSample = waveformatex.wBitsPerSample;
83 wfx.nBlockAlign = waveformatex.nBlockAlign;
84 wfx.nChannels = waveformatex.nChannels;
85 wfx.wFormatTag = waveformatex.wFormatTag;
86 wfx.nSamplesPerSec = waveformatex.nSamplesPerSec;
91 public override uint GetTotalPCMSize(int nHandle)
93 #region [ Getting PCM length ]
94 uint dlen = (uint)format.DataLengthPcm;
95 #region [ Debug info ]
96 //Debug.WriteLine( "**INTERNAL VALUE**" );
97 //Debug.WriteLine( "dlen= " + dlen );
103 public override int Seek(int nHandle, uint dwPosition)
107 public override int Decode(int nHandle, IntPtr pDest, uint szDestSize, int bLoop)
109 #region [ Decodig xa data ]
110 srcBuf = new byte[format.Blocks * format.BlockSizeXa];
111 pcmbuf = new short[format.Blocks * format.BlockSizePcm];
113 if (fs.Read(srcBuf, 0, srcBuf.Length) != srcBuf.Length)
115 string s = Path.GetFileName(filename);
116 throw new Exception( $"Failed to load xa data: {s}");
118 int ret = bjxa.Decode(srcBuf, pcmbuf, out long pcmBufLength);
120 Marshal.Copy(pcmbuf, 0, pDest, (int)format.DataLengthPcm/2);
122 #region [ alternative copy way ]
123 // if you can't use Marshal.Copy, try this.
126 // short* pWavBuf = (short*)pDest;
127 // for (int i = 0; i < format.DataLengthPcm/2; i++)
129 // *pWavBuf++ = pcmbuf[i];
134 //string shortpath = Path.GetFileName(filename);
135 //Trace.TraceInformation($"libbjxa: decode succeeded: {shortpath} = {szDestSize}");
141 #region [ Debug info ]
142 //Debug.WriteLine( "**XASTREAMHEADER**" );
143 //Debug.WriteLine( "nSrcLen= " + xastreamheader.nSrcLen );
144 //Debug.WriteLine( "nSrcUsed= " + xastreamheader.nSrcUsed );
145 //Debug.WriteLine( "nDstLen= " + xastreamheader.nDstLen );
146 //Debug.WriteLine( "nDstUsed= " + xastreamheader.nDstUsed );
152 public override void Close(int nHandle)
161 private void SaveWav(string filename)
163 long _TotalPCMSize = (uint)format.DataLengthPcm;
164 CWin32.WAVEFORMATEX _wfx = waveformatex;
166 string outfile = Path.GetFileName(filename);
167 var fs2 = new FileStream(outfile + ".wav", FileMode.Create);
168 var st = new BinaryWriter(fs2);
170 st.Write(new byte[] { 0x52, 0x49, 0x46, 0x46 }, 0, 4); // 'RIFF'
171 st.Write((int)_TotalPCMSize + 44 - 8); // filesize - 8 [byte];今は不明なので後で上書きする。
172 st.Write(new byte[] { 0x57, 0x41, 0x56, 0x45 }, 0, 4); // 'WAVE'
173 st.Write(new byte[] { 0x66, 0x6D, 0x74, 0x20 }, 0, 4); // 'fmt '
174 st.Write(new byte[] { 0x10, 0x00, 0x00, 0x00 }, 0, 4); // chunk size 16bytes
175 st.Write(new byte[] { 0x01, 0x00 }, 0, 2); // formatTag 0001 PCM
176 st.Write((short)_wfx.nChannels); // channels
177 st.Write((int)_wfx.nSamplesPerSec); // samples per sec
178 st.Write((int)_wfx.nAvgBytesPerSec); // avg bytesper sec
179 st.Write((short)_wfx.nBlockAlign); // blockalign = 16bit * mono/stereo
180 st.Write((short)_wfx.wBitsPerSample); // bitspersample = 16bits
182 st.Write(new byte[] { 0x64, 0x61, 0x74, 0x61 }, 0, 4); // 'data'
183 st.Write((int)_TotalPCMSize); // datasize
186 //var pcmbuf = new short[format.Blocks * format.BlockSizePcm];
187 //if (fs.Read(srcBuf, 0, srcBuf.Length) != srcBuf.Length)
189 // string s = Path.GetFileName(filename);
190 // throw new Exception($"Failed to load xa data: {s}");
192 //int ret = bjxa.Decode(srcBuf, pcmbuf, out long pcmBufLength);
194 int shortsize = (int)(format.Blocks * format.BlockSizePcm);
195 var pcmbuf_bytes = new byte[shortsize * 2];
196 for (int i = 0; i < shortsize; i++)
198 var b = BitConverter.GetBytes(pcmbuf[i]);
199 pcmbuf_bytes[i * 2] = b[0];
200 pcmbuf_bytes[i * 2 + 1] = b[1];
202 st.Write(pcmbuf_bytes);
203 Trace.TraceInformation($"wrote ({outfile}.wav) " + fs2.Length);
208 #region [ IDisposable implementatitons ]
210 private bool bDisposed = false;
212 // Public implementation of Dispose pattern callable by consumers.
213 public void Dispose()
216 GC.SuppressFinalize(this);
219 // Protected implementation of Dispose pattern.
220 protected virtual void Dispose(bool disposing)
225 if (srcBuf != null) srcBuf = null;
226 if (bjxa != null) bjxa = null;
233 this.bDisposed = true;