OSDN Git Service

#xxxxx Windows10の場合、「ゲームモード」を使用することを強く推奨する旨を、readmeに記載。
[dtxmania/dtxmania.git] / libbjxa / libbjxa.cs
1 /*-
2  * Copyright (C) 2018-2019  Dridi Boukelmoune
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Alternatively, you can also redistribute this library and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of either License along with this
20  * program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23 using System;
24 using System.IO;
25 using System.Reflection;
26
27 using static System.Diagnostics.Debug;
28
29 [assembly: AssemblyVersionAttribute("0.0")]
30
31 namespace bjxa
32 {
33
34         internal static class LittleEndian
35         {
36                 private static uint Read(byte[] buf, int off, int len)
37                 {
38                         Assert(buf != null);
39                         Assert(off >= 0);
40                         Assert(len > 0);
41                         Assert(off + len / 8 <= buf.Length);
42
43                         uint res = 0;
44                         int shl = 0;
45                         while (len > 0)
46                         {
47                                 res += (uint)buf[off] << shl;
48                                 shl += 8;
49                                 len -= 8;
50                                 off++;
51                         }
52
53                         return (res);
54                 }
55
56                 internal static short ReadShort(byte[] buf, int off)
57                 {
58                         return ((short)ReadUShort(buf, off));
59                 }
60
61                 internal static ushort ReadUShort(byte[] buf, int off)
62                 {
63                         return ((ushort)Read(buf, off, 16));
64                 }
65
66                 internal static uint ReadUInt(byte[] buf, int off)
67                 {
68                         return (Read(buf, off, 32));
69                 }
70
71                 internal static void Write(byte[] buf, int off, long val,
72                         int len)
73                 {
74                         Write(buf, off, (ulong)val, len);
75                 }
76
77                 internal static void Write(byte[] buf, int off, ulong val,
78                         int len)
79                 {
80                         Assert(buf != null);
81                         Assert(off >= 0);
82                         Assert(len > 0);
83                         Assert(off + len / 8 <= buf.Length);
84
85                         while (len > 0)
86                         {
87                                 buf[off] = (byte)val;
88                                 val >>= 8;
89                                 len -= 8;
90                                 off++;
91                         }
92                 }
93         }
94
95         public class Format
96         {
97                 public const int HEADER_SIZE_XA = 32;
98                 public const int HEADER_SIZE_RIFF = 44;
99
100                 internal const uint HEADER_MAGIC = 0x3144574b;
101                 internal const uint BLOCK_SAMPLES = 32;
102                 internal const uint WAVE_HEADER_LEN = 16;
103                 internal const ushort WAVE_FORMAT_PCM = 1;
104
105                 public long DataLengthPcm;
106                 public long Blocks;
107                 public uint BlockSizePcm;
108                 public uint BlockSizeXa;
109                 public ushort SamplesRate;
110                 public uint SampleBits;
111                 public uint Channels;
112
113                 public uint BlockSamples
114                 {
115                         get { return (BLOCK_SAMPLES * Channels); }
116                 }
117
118                 public long RiffHeaderLength
119                 {
120                         get { return (HEADER_SIZE_RIFF - 8 + DataLengthPcm); }
121                 }
122
123                 public uint WaveHeaderLength
124                 {
125                         get { return (WAVE_HEADER_LEN); }
126                 }
127
128                 public uint WaveFormatPcm
129                 {
130                         get { return (WAVE_FORMAT_PCM); }
131                 }
132
133                 public uint WaveByteRate
134                 {
135                         get
136                         {
137                                 return (SamplesRate * BlockSizePcm /
138                                         BLOCK_SAMPLES);
139                         }
140                 }
141
142                 public uint WaveBlockAlign
143                 {
144                         get { return (Channels * SampleBits / 8); }
145                 }
146
147                 internal Format() { }
148
149                 public byte[] WritePcm(short[] pcm, long len)
150                 {
151                         if (pcm == null)
152                                 throw new ArgumentNullException(
153                                         "bjxa.Format.WritePcm: pcm");
154                         if (len % sizeof(short) != 0)
155                                 throw new ArgumentException(
156                                         "Invalid length: {len}");
157                         int pcmLen = (int)len / sizeof(short);
158                         if (pcmLen < 0 || pcmLen > pcm.Length)
159                                 throw new ArgumentException(
160                                         "Invalid length: {len}");
161
162                         byte[] dst = new byte[pcmLen * sizeof(short)];
163
164                         int dstOff = 0, pcmOff = 0;
165                         while (pcmOff < pcmLen)
166                         {
167                                 ushort sample = (ushort)pcm[pcmOff];
168                                 pcmOff++;
169
170                                 dst[dstOff] = (byte)(sample & 0xff);
171                                 dstOff++;
172                                 dst[dstOff] = (byte)(sample >> 8);
173                                 dstOff++;
174                         }
175
176                         return (dst);
177                 }
178
179                 public int WritePcm(Stream wav, short[] pcm, long len)
180                 {
181                         if (wav == null)
182                                 throw new ArgumentNullException(
183                                         "bjxa.Format.WritePcm: wav");
184                         if (pcm == null)
185                                 throw new ArgumentNullException(
186                                         "bjxa.Format.WritePcm: pcm");
187
188                         byte[] buf = WritePcm(pcm, len);
189                         wav.Write(buf, 0, buf.Length);
190                         return (buf.Length);
191                 }
192         }
193
194         public sealed class FormatError : Exception
195         {
196                 internal FormatError(String message) : base(message) { }
197         }
198
199         internal delegate byte Inflate(DecoderState dec, short[] dst, int off,
200                 byte[] src);
201
202         internal class ChannelState
203         {
204                 internal short Prev0;
205                 internal short Prev1;
206         }
207
208         internal class DecoderState
209         {
210                 internal uint DataLength;
211                 internal uint Samples;
212                 internal ushort SamplesRate;
213                 internal uint BlockSize;
214                 internal uint Channels;
215                 internal ChannelState[] LR;
216                 internal Inflate InflateFunc;
217
218                 internal DecoderState()
219                 {
220                         LR = new ChannelState[2] {
221                                 new ChannelState(),
222                                 new ChannelState(),
223                         };
224                 }
225
226                 internal bool IsValid()
227                 {
228                         long blocks, maxSamples;
229
230                         if (DataLength == 0 || Samples == 0 ||
231                                 SamplesRate == 0 || BlockSize == 0)
232                                 return (false);
233
234                         if (Channels != 1 && Channels != 2)
235                                 return (false);
236
237                         blocks = DataLength / BlockSize;
238                         maxSamples = (Format.BLOCK_SAMPLES * DataLength) /
239                                 (BlockSize * Channels);
240
241                         if (blocks * BlockSize != DataLength)
242                                 return (false);
243
244                         if (Samples > maxSamples)
245                                 return (false);
246
247                         if (maxSamples - Samples >= Format.BLOCK_SAMPLES)
248                                 return (false);
249
250                         if (InflateFunc == null)
251                                 return (false);
252
253                         return (true);
254                 }
255
256                 internal Format ToFormat()
257                 {
258                         Assert(IsValid());
259
260                         Format fmt = new Format();
261                         fmt.DataLengthPcm = Samples * Channels *
262                                 sizeof(short);
263                         fmt.SamplesRate = SamplesRate;
264                         fmt.SampleBits = 16;
265                         fmt.Channels = Channels;
266                         fmt.BlockSizeXa = BlockSize * Channels;
267                         fmt.BlockSizePcm = Format.BLOCK_SAMPLES * Channels *
268                                 sizeof(short);
269                         fmt.Blocks = DataLength / fmt.BlockSizeXa;
270
271                         Assert(fmt.Blocks * fmt.BlockSizeXa == DataLength);
272
273                         return (fmt);
274                 }
275
276                 internal byte Inflate(short[] dst, int off, byte[] src)
277                 {
278                         return (InflateFunc(this, dst, off, src));
279                 }
280         }
281
282         public class Decoder
283         {
284                 DecoderState state;
285                 Format fmt;
286
287                 static Inflate BlockInflater(uint bits)
288                 {
289                         if (bits == 4)
290                                 return ((dec, dst, off, src) => {
291                                         Assert(off == 0 || off == 1);
292                                         Assert(Format.BLOCK_SAMPLES *
293                                                 dec.Channels == dst.Length);
294
295                                         byte profile = src[0];
296                                         int srcOff = 1;
297
298                                         for (uint n = Format.BLOCK_SAMPLES;
299                                                 n > 0; n -= 2)
300                                         {
301                                                 ushort s = src[srcOff];
302                                                 srcOff++;
303
304                                                 dst[off] =
305                                                         (short)((s & 0xf0) << 8);
306                                                 off += (int)dec.Channels;
307                                                 dst[off] =
308                                                         (short)((s & 0x0f) << 12);
309                                                 off += (int)dec.Channels;
310                                         }
311
312                                         return (profile);
313                                 });
314
315                         if (bits == 6)
316                                 return ((dec, dst, off, src) => {
317                                         Assert(off == 0 || off == 1);
318                                         Assert(Format.BLOCK_SAMPLES *
319                                                 dec.Channels == dst.Length);
320
321                                         byte profile = src[0];
322                                         int srcOff = 1;
323
324                                         for (uint n = Format.BLOCK_SAMPLES;
325                                                 n > 0; n -= 4)
326                                         {
327                                                 int s = (src[srcOff] << 16) |
328                                                         (src[srcOff + 1] << 8) |
329                                                         src[srcOff + 2];
330                                                 srcOff += 3;
331
332                                                 dst[off] = (short)
333                                                         ((s & 0x00fc0000) >> 8);
334                                                 off += (int)dec.Channels;
335                                                 dst[off] = (short)
336                                                         ((s & 0x0003f000) >> 2);
337                                                 off += (int)dec.Channels;
338                                                 dst[off] = (short)
339                                                         ((s & 0x00000fc0) << 4);
340                                                 off += (int)dec.Channels;
341                                                 dst[off] = (short)
342                                                         ((s & 0x0000003f) << 10);
343                                                 off += (int)dec.Channels;
344                                         }
345
346                                         return (profile);
347                                 });
348
349                         if (bits == 8)
350                                 return ((dec, dst, off, src) => {
351                                         Assert(off == 0 || off == 1);
352                                         Assert(Format.BLOCK_SAMPLES *
353                                                 dec.Channels == dst.Length);
354
355                                         byte profile = src[0];
356                                         int srcOff = 1;
357
358                                         for (uint n = Format.BLOCK_SAMPLES;
359                                                 n > 0; n--)
360                                         {
361                                                 dst[off] =
362                                                         (short)(src[srcOff] << 8);
363                                                 off += (int)dec.Channels;
364                                                 srcOff++;
365                                         }
366
367                                         return (profile);
368                                 });
369
370                         return (null);
371                 }
372
373                 public Format ReadHeader(byte[] xa)
374                 {
375                         if (xa == null)
376                                 throw new ArgumentNullException(
377                                         "bjxa.Decoder.ReadHeader: xa");
378                         if (xa.Length < Format.HEADER_SIZE_XA)
379                                 throw new ArgumentException(
380                                         "buffer too small");
381
382                         DecoderState tmp = new DecoderState();
383                         uint magic = LittleEndian.ReadUInt(xa, 0);
384                         tmp.DataLength = LittleEndian.ReadUInt(xa, 4);
385                         tmp.Samples = LittleEndian.ReadUInt(xa, 8);
386                         tmp.SamplesRate = LittleEndian.ReadUShort(xa, 12);
387                         uint bits = xa[14];
388                         tmp.Channels = xa[15];
389                         /* XXX: skipping loop ptr field for now */
390                         tmp.LR[0].Prev0 = LittleEndian.ReadShort(xa, 20);
391                         tmp.LR[0].Prev1 = LittleEndian.ReadShort(xa, 22);
392                         tmp.LR[1].Prev0 = LittleEndian.ReadShort(xa, 24);
393                         tmp.LR[1].Prev1 = LittleEndian.ReadShort(xa, 26);
394                         /* XXX: ignoring padding for now */
395
396                         tmp.InflateFunc = BlockInflater(bits);
397                         tmp.BlockSize = bits * 4 + 1;
398
399                         if (magic != Format.HEADER_MAGIC || !tmp.IsValid())
400                                 throw new FormatError("Invalid XA header");
401
402                         state = tmp;
403                         fmt = state.ToFormat();
404
405                         return (state.ToFormat());
406                 }
407
408                 public Format ReadHeader(Stream xa)
409                 {
410                         if (xa == null)
411                                 throw new ArgumentNullException(
412                                         "bjxa.Decoder.ReadHeader: xa");
413
414                         byte[] hdr = new byte[Format.HEADER_SIZE_XA];
415                         if (xa.Read(hdr, 0, hdr.Length) != hdr.Length)
416                                 throw new EndOfStreamException("XA header");
417
418                         return ReadHeader(hdr);
419                 }
420
421                 public byte[] WriteRiffHeader()
422                 {
423                         if (state == null)
424                                 throw new InvalidOperationException(
425                                         "Decoder not ready");
426                         Format fmt = state.ToFormat();
427                         byte[] hdr = new byte[Format.HEADER_SIZE_RIFF];
428                         LittleEndian.Write(hdr, 0, 0x46464952, 32); /* RIFF */
429                         LittleEndian.Write(hdr, 4, fmt.RiffHeaderLength, 32);
430                         LittleEndian.Write(hdr, 8, 0x45564157, 32); /* WAVE */
431                         LittleEndian.Write(hdr, 12, 0x20746d66, 32); /* fmt  */
432                         LittleEndian.Write(hdr, 16, fmt.WaveHeaderLength, 32);
433                         LittleEndian.Write(hdr, 20, fmt.WaveFormatPcm, 16);
434                         LittleEndian.Write(hdr, 22, fmt.Channels, 16);
435                         LittleEndian.Write(hdr, 24, fmt.SamplesRate, 32);
436                         LittleEndian.Write(hdr, 28, fmt.WaveByteRate, 32);
437                         LittleEndian.Write(hdr, 32, fmt.WaveBlockAlign, 16);
438                         LittleEndian.Write(hdr, 34, fmt.SampleBits, 16);
439                         LittleEndian.Write(hdr, 36, 0x61746164, 32); /* data */
440                         LittleEndian.Write(hdr, 40, fmt.DataLengthPcm, 32);
441                         return (hdr);
442                 }
443
444                 public int WriteRiffHeader(Stream wav)
445                 {
446                         if (state == null)
447                                 throw new InvalidOperationException(
448                                         "Decoder not ready");
449                         if (wav == null)
450                                 throw new ArgumentNullException(
451                                         "bjxa.Decoder.WriteRiffHeader: wav");
452
453                         byte[] hdr = WriteRiffHeader();
454                         wav.Write(hdr, 0, hdr.Length);
455                         return (hdr.Length);
456                 }
457
458                 private readonly short[,] GainFactor = {
459                         {  0,    0},
460                         {240,    0},
461                         {460, -208},
462                         {392, -220},
463                         {488, -240},
464                 };
465
466                 private void DecodeInflated(short[] pcm, int off, byte prof)
467                 {
468                         Assert(off == 0 || off == 1);
469                         Assert(Format.BLOCK_SAMPLES * state.Channels ==
470                                 pcm.Length);
471
472                         int factor = prof >> 4;
473                         int range = prof & 0x0f;
474
475                         if (factor >= GainFactor.Length)
476                                 throw new FormatError(
477                                         $"Invalid factor: {factor}");
478
479                         ChannelState chan = state.LR[off];
480                         short k0 = GainFactor[factor, 0];
481                         short k1 = GainFactor[factor, 1];
482
483                         for (uint i = Format.BLOCK_SAMPLES; i > 0; i--)
484                         {
485                                 int ranged = pcm[off] >> range;
486                                 int gain = (chan.Prev0 * k0) +
487                                         (chan.Prev1 * k1);
488                                 int sample = ranged + gain / 256;
489
490                                 sample = Math.Max(sample, Int16.MinValue);
491                                 sample = Math.Min(sample, Int16.MaxValue);
492
493                                 pcm[off] = (short)sample;
494                                 chan.Prev1 = chan.Prev0;
495                                 chan.Prev0 = (short)sample;
496
497                                 off += (int)state.Channels;
498                         }
499                 }
500
501                 public int Decode(byte[] xa, short[] pcm, out long pcm_data)
502                 {
503                         if (state == null)
504                                 throw new InvalidOperationException(
505                                         "Decoder not ready");
506                         if (xa == null)
507                                 throw new ArgumentNullException(
508                                         "bjxa.Decoder.Decode: xa");
509                         if (pcm == null)
510                                 throw new ArgumentNullException(
511                                         "bjxa.Decoder.Decode: pcm");
512                         Assert(fmt != null);
513                         if (fmt.Blocks == 0)
514                                 throw new InvalidOperationException(
515                                         "XA stream fully decoded");
516
517                         int xaLen = xa.Length * sizeof(byte);
518                         int xaOff = 0;
519                         int pcmLen = pcm.Length * sizeof(short);
520                         int pcmOff = 0;
521                         byte[] xaBuf = new byte[state.BlockSize];
522                         short[] pcmBuf = new short[fmt.BlockSamples];
523                         int blocks = 0;
524                         long pcm_block = Math.Min(fmt.BlockSizePcm,
525                                 fmt.DataLengthPcm);
526                         pcm_data = 0;
527
528                         while (fmt.Blocks > 0 && xaLen >= fmt.BlockSizeXa &&
529                                 pcmLen >= pcm_block)
530                         {
531
532                                 Array.Copy(xa, xaOff, xaBuf, 0, xaBuf.Length);
533                                 byte prof = state.Inflate(pcmBuf, 0, xaBuf);
534                                 DecodeInflated(pcmBuf, 0, prof);
535
536                                 xaOff += xaBuf.Length;
537                                 xaLen -= xaBuf.Length;
538
539                                 if (state.Channels == 2)
540                                 {
541                                         Array.Copy(xa, xaOff, xaBuf, 0,
542                                                 xaBuf.Length);
543                                         prof = state.Inflate(pcmBuf, 1, xaBuf);
544                                         DecodeInflated(pcmBuf, 1, prof);
545
546                                         xaOff += xaBuf.Length;
547                                         xaLen -= xaBuf.Length;
548                                 }
549
550                                 Array.Copy(pcmBuf, 0, pcm, pcmOff,
551                                         pcm_block / sizeof(short));
552                                 pcmOff += pcmBuf.Length;
553                                 pcmLen -= (int)pcm_block;
554                                 pcm_data += pcm_block;
555                                 fmt.DataLengthPcm -= pcm_block;
556
557                                 pcm_block = Math.Min(fmt.BlockSizePcm,
558                                         fmt.DataLengthPcm);
559
560                                 blocks++;
561                                 fmt.Blocks--;
562                         }
563
564                         return (blocks);
565                 }
566         }
567 }