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 int nHandle = -1;
21 private bjxa.Decoder bjxa = new bjxa.Decoder();
22 private bjxa.Format format = null;
23 private FileStream fs;
25 public override int Open(string filename)
27 this.filename = filename;
29 #region [ Reading XA headers, then store it ]
30 fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // Need to set FileShare flag, to avoid locking after Closer()
31 format = bjxa.ReadHeader(fs);
32 //string xaid = Encoding.ASCII.GetString( xah.id );
34 #region [ Debug info ]
35 //Debug.WriteLine( "**XAHEADER**" );
36 //Debug.WriteLine( "id= " + xaheader.id.ToString( "X8" ) );
37 //Debug.WriteLine( "nDataLen= " + xaheader.nDataLen.ToString( "X8" ) );
38 //Debug.WriteLine( "nSamples= " + xaheader.nSamples.ToString( "X8" ) );
39 //Debug.WriteLine( "nSamplesPerSec= " + xaheader.nSamplesPerSec.ToString( "X4" ) );
40 //Debug.WriteLine( "nBits= " + xaheader.nBits.ToString( "X2" ) );
41 //Debug.WriteLine( "nChannels= " + xaheader.nChannels.ToString( "X2" ) );
42 //Debug.WriteLine( "nLoopPtr= " + xaheader.nLoopPtr.ToString( "X8" ) );
43 //Debug.WriteLine( "befL[0]= " + xaheader.befL[ 0 ].ToString( "X4" ) );
44 //Debug.WriteLine( "befL[1]= " + xaheader.befL[ 1 ].ToString( "X4" ) );
45 //Debug.WriteLine( "befR[0]= " + xaheader.befR[ 0 ].ToString( "X4" ) );
46 //Debug.WriteLine( "befR[1]= " + xaheader.befR[ 1 ].ToString( "X4" ) );
51 #region [ Getting WAVEFORMEX info ]
52 waveformatex = new CWin32.WAVEFORMATEX();
54 waveformatex.wFormatTag = (ushort)format.WaveFormatPcm;
55 waveformatex.nChannels = (ushort)format.Channels;
56 waveformatex.nSamplesPerSec = format.SamplesRate;
57 waveformatex.nAvgBytesPerSec = format.WaveByteRate;
58 waveformatex.nBlockAlign = (ushort)format.WaveBlockAlign;
59 waveformatex.wBitsPerSample = (ushort)format.SampleBits;
60 waveformatex.cbSize = 0;
62 #region [ Debug info ]
63 //Debug.WriteLine( "**WAVEFORMATEX**" );
64 //Debug.WriteLine( "wFormatTag= " + waveformatex.wFormatTag.ToString( "X4" ) );
65 //Debug.WriteLine( "nChannels = " + waveformatex.nChannels.ToString( "X4" ) );
66 //Debug.WriteLine( "nSamplesPerSec= " + waveformatex.nSamplesPerSec.ToString( "X8" ) );
67 //Debug.WriteLine( "nAvgBytesPerSec= " + waveformatex.nAvgBytesPerSec.ToString( "X8" ) );
68 //Debug.WriteLine( "nBlockAlign= " + waveformatex.nBlockAlign.ToString( "X4" ) );
69 //Debug.WriteLine( "wBitsPerSample= " + waveformatex.wBitsPerSample.ToString( "X4" ) );
70 //Debug.WriteLine( "cbSize= " + waveformatex.cbSize.ToString( "X4" ) );
74 return 0x7FFFFFFF; // Anyway, return non-zero value
76 public override int GetFormat(int nHandle, ref CWin32.WAVEFORMATEX wfx)
78 #region [ Copying WAVEFORMATEX structure data ]
79 wfx.nAvgBytesPerSec = waveformatex.nAvgBytesPerSec;
80 wfx.wBitsPerSample = waveformatex.wBitsPerSample;
81 wfx.nBlockAlign = waveformatex.nBlockAlign;
82 wfx.nChannels = waveformatex.nChannels;
83 wfx.wFormatTag = waveformatex.wFormatTag;
84 wfx.nSamplesPerSec = waveformatex.nSamplesPerSec;
89 public override uint GetTotalPCMSize(int nHandle)
91 #region [ Getting PCM length ]
92 uint dlen = (uint)format.DataLengthPcm;
93 #region [ Debug info ]
94 //Debug.WriteLine( "**INTERNAL VALUE**" );
95 //Debug.WriteLine( "dlen= " + dlen );
101 public override int Seek(int nHandle, uint dwPosition)
105 public override int Decode(int nHandle, IntPtr pDest, uint szDestSize, int bLoop)
107 #region [ Decodig xa data ]
108 srcBuf = new byte[format.Blocks * format.BlockSizeXa];
109 var pcmbuf = new short[format.Blocks * format.BlockSizePcm];
111 if (fs.Read(srcBuf, 0, srcBuf.Length) != srcBuf.Length)
113 string s = Path.GetFileName(filename);
114 throw new Exception( $"Failed to load xa data: {s}");
116 int ret = bjxa.Decode(srcBuf, pcmbuf, out long pcmBufLength);
118 Marshal.Copy(pcmbuf, 0, pDest, (int)format.DataLengthPcm/2);
120 #region [ alternative copy way ]
121 // if you can't use Marshal.Copy, try this.
124 // short* pWavBuf = (short*)pDest;
125 // for (int i = 0; i < format.DataLengthPcm/2; i++)
127 // *pWavBuf++ = pcmbuf[i];
132 //string shortpath = Path.GetFileName(filename);
133 //Trace.TraceInformation($"libbjxa: decode succeeded: {shortpath} = {szDestSize}");
138 #region [ Debug info ]
139 //Debug.WriteLine( "**XASTREAMHEADER**" );
140 //Debug.WriteLine( "nSrcLen= " + xastreamheader.nSrcLen );
141 //Debug.WriteLine( "nSrcUsed= " + xastreamheader.nSrcUsed );
142 //Debug.WriteLine( "nDstLen= " + xastreamheader.nDstLen );
143 //Debug.WriteLine( "nDstUsed= " + xastreamheader.nDstUsed );
149 public override void Close(int nHandle)
156 #region [ IDisposable implementatitons ]
158 private bool bDisposed = false;
160 // Public implementation of Dispose pattern callable by consumers.
161 public void Dispose()
164 GC.SuppressFinalize(this);
167 // Protected implementation of Dispose pattern.
168 protected virtual void Dispose(bool disposing)
173 if (srcBuf != null) srcBuf = null;
174 if (bjxa != null) bjxa = null;
181 this.bDisposed = true;