mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
Add .cwav + decoder [RADIO ZONDE (PC)]
This commit is contained in:
parent
64c95f6842
commit
3d87f31361
@ -628,6 +628,8 @@ are used in few games.
|
||||
- OKI 4-bit ADPCM (16-bit output, 4-shift, PC-FX)
|
||||
- Ubisoft 4/6-bit ADPCM
|
||||
- Tiger Game.com ADPCM
|
||||
- LucasArts iMUSE VBR ADPCM
|
||||
- CompressWave Huffman ADPCM
|
||||
- SDX2 2:1 Squareroot-Delta-Exact compression DPCM
|
||||
- CBD2 2:1 Cuberoot-Delta-Exact compression DPCM
|
||||
- Activision EXAKT SASSC DPCM
|
||||
@ -641,6 +643,7 @@ are used in few games.
|
||||
- MPEG MP1/2/3 (standard, AHX, XVAG, FSB, AWC, P3D, etc)
|
||||
- ITU-T G.722.1 annex C (Polycom Siren 14)
|
||||
- ITU-T G.719 annex B (Polycom Siren 22)
|
||||
- Electronic Arts EASpeex
|
||||
- Electronic Arts EALayer3
|
||||
- Electronic Arts EA-XMA
|
||||
- Sony ATRAC3, ATRAC3plus
|
||||
|
@ -270,6 +270,17 @@ void seek_imuse(imuse_codec_data* data, int32_t num_sample);
|
||||
void free_imuse(imuse_codec_data* data);
|
||||
|
||||
|
||||
/* compresswave_decoder */
|
||||
typedef struct compresswave_codec_data compresswave_codec_data;
|
||||
|
||||
compresswave_codec_data* init_compresswave(STREAMFILE* sf);
|
||||
void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_compresswave(compresswave_codec_data* data);
|
||||
void seek_compresswave(compresswave_codec_data* data, int32_t num_sample);
|
||||
void free_compresswave(compresswave_codec_data* data);
|
||||
STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data);
|
||||
|
||||
|
||||
/* ea_mt_decoder*/
|
||||
typedef struct ea_mt_codec_data ea_mt_codec_data;
|
||||
|
||||
|
998
src/coding/compresswave_decode_lib.c
Normal file
998
src/coding/compresswave_decode_lib.c
Normal file
@ -0,0 +1,998 @@
|
||||
#include "compresswave_decode_lib.h"
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* Decodes CWav (CompressWave) audio codec, based on original delphi/pascal source code by Ko-Ta:
|
||||
* - http://kota.dokkoisho.com/
|
||||
* - http://kota.dokkoisho.com/library/CompressWave.zip
|
||||
* - https://web.archive.org/web/20180819144937/http://d.hatena.ne.jp/Ko-Ta/20070318/p1
|
||||
* (no license given)
|
||||
* Apparently found in few Japanese (doujin?) games around 1995-2002, most notably RADIO ZONDE.
|
||||
*
|
||||
* This is mostly a simple re-implementation following original code, basically Pascal-classes-to-plain-C
|
||||
* because why not. Only decoder part is replicated (some writting/loading/etc stuff removed or cleaned up).
|
||||
* Results should be byte-exact (all is int math).
|
||||
* **some parts like internal looping weren't tested (no valid files)
|
||||
*
|
||||
* Codec is basically huffman-coded ADPCM, that includes diff table and huffman tree setup in
|
||||
* CWav header. Described by the author as being "big, heavy and with bad sound quality".
|
||||
* An oddity is that mono files are output as fake stereo (repeats L/R), this is correct and agrees
|
||||
* with PCM totals in header. Output sample rate is always 44100 and files marked as 22050 or mono just
|
||||
* decode slightly differently. Curiously PCM output size in header may be not be multiple of 4, meaning
|
||||
* files that end with garbage half-a-sample (L sample = 0, nothing for R).
|
||||
*/
|
||||
|
||||
|
||||
/* ************************************************************************* */
|
||||
/* common */
|
||||
/* ************************************************************************* */
|
||||
// pascal reader simulated in C
|
||||
typedef struct {
|
||||
STREAMFILE* File;
|
||||
int64_t Position;
|
||||
int64_t Size;
|
||||
} TStream;
|
||||
|
||||
static void TStream_Read_Uint32(TStream* this, uint32_t* value) {
|
||||
uint8_t buf[0x4] = {0};
|
||||
|
||||
read_streamfile(buf, this->Position, sizeof(buf), this->File);
|
||||
this->Position += 0x4;
|
||||
|
||||
*value = get_u32le(buf);
|
||||
}
|
||||
|
||||
|
||||
/* ************************************************************************* */
|
||||
/* HuffLib.pas */
|
||||
/* ************************************************************************* */
|
||||
|
||||
#define CW_TRUE 1
|
||||
#define CW_FALSE 0
|
||||
|
||||
typedef enum { nsEmpty, nsBranch, nsLeaf, nsRoot } TNodeState;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//structure declaration
|
||||
|
||||
//node structure for huffman tree
|
||||
typedef struct {
|
||||
uint8_t Value; //value (0..255)
|
||||
int32_t Weight; //weight value used during huffman tree creation
|
||||
TNodeState State; //state
|
||||
int32_t Link[2]; //bidimensional tree L/R path (-1: unused, >=0: index)
|
||||
} THuffTreeNode;
|
||||
|
||||
//header info for file writting
|
||||
typedef struct {
|
||||
char HedChar[4]; //head info
|
||||
int32_t Version; //Version
|
||||
uint32_t HistGraph[256]; //appearance rate
|
||||
int64_t FileSize; //file size
|
||||
} THuffHedState;
|
||||
|
||||
//for jumping to arbitrary places (^^), various usages
|
||||
typedef struct {
|
||||
uint32_t BitBuf;
|
||||
int32_t BitCount;
|
||||
int64_t StreamPos;
|
||||
uint32_t CipherBuf;
|
||||
} THuffPositionData;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//huffman encoding class
|
||||
//handled values are 0~255 of 1 byte.
|
||||
//takes appearance rate and makes a huffman tree for encoding
|
||||
|
||||
//EXTRA: external lib part, but not really needed so all is static
|
||||
|
||||
typedef struct {
|
||||
//control
|
||||
TStream Buff; // target stream
|
||||
int64_t BeginPos; // encoded area
|
||||
int Mode; // (0=initial, 1=read, 2=write)
|
||||
|
||||
//bit IO
|
||||
int IoCount; // 0..initial state, 1..read, 2..write
|
||||
uint32_t BitBuf; // held buffer
|
||||
int BitCount; // processed bit count
|
||||
#if 0
|
||||
int64_t BitWriteLen; // written size
|
||||
#endif
|
||||
uint32_t CipherBuf;
|
||||
|
||||
//huffman
|
||||
THuffTreeNode Node[512]; //tree structure
|
||||
uint8_t Code[256][256]; //fork support
|
||||
int32_t Root; //root
|
||||
|
||||
//huffman cipher bits
|
||||
uint32_t CipherList[16];
|
||||
|
||||
//header info
|
||||
THuffHedState Hed;
|
||||
} THuff;
|
||||
|
||||
|
||||
//related to huffman encoding
|
||||
static void THuff_InitHuffTree(THuff* this); //initializes tree
|
||||
static int THuff_InsertHuffNode(THuff* this, int v, int w, TNodeState s, int b1, int b2); //add node to tree
|
||||
static void THuff_MakeHuffTree(THuff* this);
|
||||
|
||||
//related to single bit IO
|
||||
static void THuff_BeginBitIO(THuff* this);
|
||||
static void THuff_EndBitIO(THuff* this);
|
||||
static int THuff_ReadBit(THuff* this);
|
||||
static uint32_t THuff__ROR(uint32_t src, uint32_t shift);
|
||||
|
||||
static THuff* THuff_Create(TStream* buf); // creation
|
||||
static void THuff_Free(THuff* this); // release the power
|
||||
static void THuff_SetCipherCode(THuff* this, uint32_t msk); // encryption mask bits
|
||||
//functions for reading
|
||||
static void THuff_BeginRead(THuff* this);
|
||||
static int THuff_Read(THuff* this);
|
||||
|
||||
#if 0
|
||||
static int64_t THuff_GetFileSize(THuff* this); // get file size before encoding
|
||||
static int THuff_GetEOF(THuff* this); // EOF detection
|
||||
#endif
|
||||
static void THuff_MoveBeginPosition(THuff* this); // return to initial state
|
||||
static void THuff_GetPositionData(THuff* this, THuffPositionData* s); // secret
|
||||
static void THuff_SetPositionData(THuff* this, THuffPositionData* s);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//create
|
||||
static THuff* THuff_Create(TStream* buf) {
|
||||
THuff* this = malloc(sizeof(THuff));
|
||||
if (!this) return NULL;
|
||||
|
||||
//define stream
|
||||
this->Buff = *buf;
|
||||
|
||||
//initialization
|
||||
THuff_InitHuffTree(this);
|
||||
memcpy(this->Hed.HedChar, "HUF\0", 0x4);
|
||||
this->Hed.Version = 1;
|
||||
this->Hed.FileSize = 0;
|
||||
|
||||
//set cipher bits
|
||||
this->CipherBuf = 0;
|
||||
THuff_SetCipherCode(this, 0x0);
|
||||
|
||||
//mode
|
||||
this->Mode = 0;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//free
|
||||
static void THuff_Free(THuff* this) {
|
||||
if (this == NULL) return;
|
||||
if (this->Mode == 2)
|
||||
THuff_EndBitIO(this);
|
||||
free(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//init tree structure (unused state)
|
||||
static void THuff_InitHuffTree(THuff* this) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 512; i++) {
|
||||
this->Node[i].State = nsEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//add node to huffman tree
|
||||
static int THuff_InsertHuffNode(THuff* this, int v, int w, TNodeState s, int b1, int b2) {
|
||||
int result = 0;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while ((this->Node[i].State != nsEmpty) && (i < 512)) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == 512) {
|
||||
result = -1;
|
||||
return result; //exit;
|
||||
}
|
||||
|
||||
this->Node[i].Value = v & 0xFF; //BYTE(v);
|
||||
this->Node[i].Weight = w;
|
||||
this->Node[i].State = s;
|
||||
this->Node[i].Link[0] = b1;
|
||||
if (this->Node[i].Link[0] > 511) {
|
||||
return -1;//? //halt;
|
||||
}
|
||||
this->Node[i].Link[1] = b2;
|
||||
if (this->Node[i].Link[1] > 511) {
|
||||
return -1;//? //halt;
|
||||
}
|
||||
//return entry number
|
||||
result = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//reads and expands huffman-encoded data
|
||||
static int THuff_Read(THuff* this) {
|
||||
int i;
|
||||
|
||||
i = this->Root;
|
||||
while (this->Node[i].State != nsLeaf) {
|
||||
i = this->Node[i].Link[THuff_ReadBit(this)];
|
||||
}
|
||||
|
||||
return this->Node[i].Value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//creates fork code from tree
|
||||
|
||||
//finds node of lowest weight
|
||||
static int THuff_MakeHuffTree_SerchMinNode(THuff* this, int* tNode) {
|
||||
int ii, aaa1, aaa2;
|
||||
|
||||
aaa1 = 0xFFFFFFF;
|
||||
aaa2 = 0;
|
||||
for (ii = 0 ; ii < 256; ii++) {
|
||||
if (tNode[ii] != -1) {
|
||||
if (this->Node[tNode[ii]].Weight < aaa1) {
|
||||
aaa2 = ii;
|
||||
aaa1 = this->Node[tNode[ii]].Weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
return aaa2;
|
||||
}
|
||||
|
||||
//finds closest node
|
||||
static int THuff_MakeHuffTree_SerchNearNode(THuff* this, int* tNode, int pos) {
|
||||
int ii, aaa1, aaa2;
|
||||
|
||||
aaa1 = 0xFFFFFFF;
|
||||
aaa2 = 0;
|
||||
for (ii = 0 ; ii < 256; ii++) {
|
||||
if (tNode[ii] != -1) {
|
||||
if ((abs(this->Node[tNode[ii]].Weight - this->Node[tNode[pos]].Weight) < aaa1) && (pos != ii)) {
|
||||
aaa2 = ii;
|
||||
aaa1 = this->Node[tNode[ii]].Weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
return aaa2;
|
||||
}
|
||||
|
||||
static void THuff_MakeHuffTree_MakeHuffCodeFromTree(THuff* this, uint8_t* tCode1, int* tCodePos, int pos) {
|
||||
int ii, aaa1;
|
||||
|
||||
if (this->Node[pos].State == nsLeaf) { //found
|
||||
tCode1[*tCodePos] = 0xFF;
|
||||
aaa1 = this->Node[pos].Value;
|
||||
for (ii = 0; ii < 256; ii++) {
|
||||
this->Code[aaa1][ii] = tCode1[ii];
|
||||
}
|
||||
}
|
||||
else { //not
|
||||
if (this->Node[pos].Link[0] != -1) {
|
||||
tCode1[*tCodePos] = 0;
|
||||
(*tCodePos)++;
|
||||
THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, tCodePos, this->Node[pos].Link[0]);
|
||||
}
|
||||
|
||||
if (this->Node[pos].Link[1] != -1) {
|
||||
tCode1[*tCodePos] = 1;
|
||||
(*tCodePos)++;
|
||||
THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, tCodePos, this->Node[pos].Link[1]);
|
||||
}
|
||||
}
|
||||
|
||||
(*tCodePos)--;
|
||||
}
|
||||
|
||||
// creates huffman tree/codes from apparance rate (0..255)
|
||||
static void THuff_MakeHuffTree(THuff* this) {
|
||||
int i, aa1, aa2, aa3;
|
||||
int tCodePos;
|
||||
uint8_t tCode1[257];
|
||||
#if 0
|
||||
uint8_t tCode2[257];
|
||||
#endif
|
||||
int tNode[257];
|
||||
|
||||
//initializes huffman tree
|
||||
THuff_InitHuffTree(this);
|
||||
for (i = 0; i < 256; i++) {
|
||||
tNode[i] = -1;
|
||||
tCode1[i] = 0;
|
||||
#if 0
|
||||
tCode2[i] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//adds child nodes + comparison target nodes
|
||||
for (i = 0; i < 256; i++) {
|
||||
tNode[i] = THuff_InsertHuffNode(this, i, this->Hed.HistGraph[i], nsLeaf, -1, -1);
|
||||
}
|
||||
|
||||
//creates optimal tree
|
||||
for (i = 0; i < 256 - 1; i++) {
|
||||
//find smallest node
|
||||
aa1 = THuff_MakeHuffTree_SerchMinNode(this, tNode);
|
||||
//find value closest to smallest node
|
||||
aa2 = THuff_MakeHuffTree_SerchNearNode(this, tNode, aa1);
|
||||
//make new node joining both together
|
||||
aa3 = THuff_InsertHuffNode(this, -1, this->Node[tNode[aa1]].Weight + this->Node[tNode[aa2]].Weight, nsBranch, tNode[aa1], tNode[aa2]);
|
||||
//remove aa1/2 from comparison target nodes.
|
||||
tNode[aa1] = -1;
|
||||
tNode[aa2] = -1;
|
||||
//add created node to comparison target nodes
|
||||
tNode[aa1] = aa3;
|
||||
}
|
||||
|
||||
//finally make added node top of the tree
|
||||
this->Root = aa3;
|
||||
|
||||
//create stack for data expansion from tree info
|
||||
tCodePos = 0;
|
||||
THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, &tCodePos, this->Root);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//bit IO start process
|
||||
static void THuff_BeginBitIO(THuff* this) {
|
||||
this->IoCount = 0;
|
||||
this->BitBuf = 0;
|
||||
this->BitCount = 32;
|
||||
#if 0
|
||||
this->BitWriteLen = 0;
|
||||
#endif
|
||||
this->CipherBuf = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//bit IO end process
|
||||
static void THuff_EndBitIO(THuff* this) {
|
||||
#if 0
|
||||
if (this->IoCount == 2 && this->BitCount > 0) {
|
||||
this->BitBuf = this->BitBuf ^ this->CipherBuf;
|
||||
TStream_Write(this->Buff, BitBuf,4);
|
||||
}
|
||||
#endif
|
||||
THuff_BeginBitIO(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//read 1 bit from file
|
||||
static int THuff_ReadBit(THuff* this) {
|
||||
int result;
|
||||
uint32_t aaa;
|
||||
|
||||
if (this->BitCount == 32) {
|
||||
this->IoCount = 1; //ReadMode
|
||||
if (this->Buff.Position < this->Buff.Size) {
|
||||
//read
|
||||
TStream_Read_Uint32(&this->Buff, &aaa); //Buff.Read(aaa,sizeof(DWORD));
|
||||
this->BitBuf = aaa ^ this->CipherBuf;
|
||||
|
||||
//decryption phase
|
||||
this->CipherBuf = THuff__ROR(this->CipherBuf, aaa & 7);
|
||||
this->CipherBuf = this->CipherBuf ^ this->CipherList[aaa & 7];
|
||||
}
|
||||
this->BitCount = 0;
|
||||
}
|
||||
|
||||
//return 1 bit
|
||||
result = this->BitBuf & 1;
|
||||
this->BitBuf = this->BitBuf >> 1;
|
||||
|
||||
//advance BitCount
|
||||
this->BitCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//starts reading encoded data from stream
|
||||
|
||||
static void TStream_Read_THuffHedState(TStream* this, THuffHedState* Hed) {
|
||||
uint8_t buf[0x410];
|
||||
int i;
|
||||
|
||||
read_streamfile(buf, this->Position, sizeof(buf), this->File);
|
||||
this->Position += sizeof(buf);
|
||||
|
||||
/* 0x00: string size (always 3) */
|
||||
memcpy(Hed->HedChar, buf+0x01, 0x03);
|
||||
Hed->Version = get_u32le(buf+0x04);
|
||||
for (i = 0; i < 256; i++) {
|
||||
Hed->HistGraph[i] = get_u32le(buf+0x08 + i*0x04);
|
||||
}
|
||||
Hed->FileSize = get_u64le(buf+0x408); /* seems always 0 */
|
||||
}
|
||||
|
||||
static void THuff_BeginRead(THuff* this) {
|
||||
TStream_Read_THuffHedState(&this->Buff, &this->Hed); //Buff.Read(Hed,sizeof(THuffHedState));
|
||||
THuff_MakeHuffTree(this);
|
||||
this->BeginPos = this->Buff.Position;
|
||||
THuff_BeginBitIO(this);
|
||||
this->Mode = 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//------------------------------------------------------------------------------
|
||||
//get file size before encoding
|
||||
static int64_t THuff_GetFileSize(THuff* this) {
|
||||
return this->Hed.FileSize;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//EOF detection
|
||||
static int THuff_GetEOF(THuff* this) {
|
||||
if (this->Buff.Position < this->Buff.Size)
|
||||
return CW_FALSE;
|
||||
else
|
||||
return CW_TRUE;
|
||||
}
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
//return to initial positon
|
||||
static void THuff_MoveBeginPosition(THuff* this) {
|
||||
THuff_EndBitIO(this);
|
||||
this->Buff.Position = this->BeginPos;
|
||||
THuff_BeginBitIO(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void THuff_GetPositionData(THuff* this, THuffPositionData* s) {
|
||||
s->BitBuf = this->BitBuf;
|
||||
s->BitCount = this->BitCount;
|
||||
s->StreamPos = this->Buff.Position;
|
||||
s->CipherBuf = this->CipherBuf;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void THuff_SetPositionData(THuff* this, THuffPositionData* s) {
|
||||
this->BitBuf = s->BitBuf;
|
||||
this->BitCount = s->BitCount;
|
||||
this->Buff.Position = s->StreamPos;
|
||||
this->CipherBuf = s->CipherBuf;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void THuff_SetCipherCode(THuff* this, uint32_t msk) {
|
||||
//creates mask list
|
||||
this->CipherList[0] = msk / 3;
|
||||
this->CipherList[1] = msk / 17;
|
||||
this->CipherList[2] = msk / 7;
|
||||
this->CipherList[3] = msk / 5;
|
||||
this->CipherList[4] = msk / 3;
|
||||
this->CipherList[5] = msk / 11;
|
||||
this->CipherList[6] = msk / 13;
|
||||
this->CipherList[7] = msk / 19;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static uint32_t THuff__ROR(uint32_t src, uint32_t shift) {
|
||||
uint8_t num = shift % 0xFF;
|
||||
return ((uint32_t)src >> num) | ((uint32_t)src << (32 - num));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// CompressWaveLib.pas
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
#define PW_MAXVOLUME 0xFFFFFFF //don't change
|
||||
|
||||
|
||||
//proprietary compression file header
|
||||
typedef struct {
|
||||
//RIFF chunk
|
||||
char HedChar[8]; // 'CmpWave'
|
||||
uint32_t Channel; // 2(STEREO) / 1(MONO)
|
||||
uint32_t Sample; // 44100Hz / 22050Hz
|
||||
uint32_t Bit; // 16bit
|
||||
int32_t Tbl[256]; // conversion table value
|
||||
int64_t UnPressSize; // decompressed data size
|
||||
int64_t LoopStart; // loop start/end position
|
||||
int64_t LoopEnd;
|
||||
uint8_t LoopCount; // loop times
|
||||
char MusicTitle[128*2]; // song name
|
||||
char MusicArtist[128*2]; // composer
|
||||
} PRESSWAVEDATAHED;
|
||||
|
||||
//for writting
|
||||
typedef struct {
|
||||
short RBuf;
|
||||
short LBuf;
|
||||
} TLRWRITEBUFFER;
|
||||
|
||||
|
||||
//compression data class
|
||||
struct TCompressWaveData {
|
||||
//rendering flag (sets during rendering)
|
||||
int NowRendering;
|
||||
//flag for playback
|
||||
int32_t Faa1;
|
||||
int32_t Faa2;
|
||||
int32_t Fvv1;
|
||||
int32_t Fvv2;
|
||||
|
||||
int32_t FVolume;
|
||||
int32_t Ffade;
|
||||
int32_t FSetVolume;
|
||||
|
||||
int FEndLoop;
|
||||
int32_t FLoop;
|
||||
int FPlay;
|
||||
int64_t FWavePosition;
|
||||
int64_t FWaveLength;
|
||||
//flag for 22050kHz
|
||||
int32_t LBackBuf;
|
||||
int32_t RBackBuf;
|
||||
//for restoration
|
||||
THuffPositionData PosData;
|
||||
int32_t LPFaa1;
|
||||
int32_t LPFaa2;
|
||||
int32_t LPFvv1;
|
||||
int32_t LPFvv2;
|
||||
//cipher code
|
||||
uint32_t CipherCode;
|
||||
//hafu-hafu-hafuman
|
||||
THuff* RH;
|
||||
|
||||
#if 0
|
||||
TMemoryStream Data;
|
||||
#endif
|
||||
PRESSWAVEDATAHED Hed;
|
||||
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//create
|
||||
TCompressWaveData* TCompressWaveData_Create() {
|
||||
TCompressWaveData* this = malloc(sizeof(TCompressWaveData));
|
||||
if (!this) return NULL;
|
||||
#if 0
|
||||
this->Data = NULL;
|
||||
#endif
|
||||
this->RH = NULL;
|
||||
this->FWavePosition = 0;
|
||||
this->FWaveLength = 0;
|
||||
this->FVolume = PW_MAXVOLUME;
|
||||
this->FSetVolume = PW_MAXVOLUME;
|
||||
this->Ffade = 0;
|
||||
this->FEndLoop = CW_FALSE;
|
||||
this->FPlay = CW_FALSE;
|
||||
this->NowRendering = CW_FALSE;
|
||||
TCompressWaveData_SetCipherCode(this, 0);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//free
|
||||
void TCompressWaveData_Free(TCompressWaveData* this) {
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
//EXTRA: presumably for threading but OG lib doesn't properly set this to false on all errors
|
||||
#if 0
|
||||
//sync
|
||||
while (this->NowRendering) {
|
||||
;
|
||||
}
|
||||
#endif
|
||||
//free
|
||||
if (this->RH != NULL)
|
||||
THuff_Free(this->RH);
|
||||
#if 0
|
||||
if (this->Data != NULL)
|
||||
TMemoryStream_Free(this->Data);
|
||||
#endif
|
||||
free(this);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//outpus 44100/16bit/stereo waveform to designed buffer
|
||||
|
||||
void TCompressWaveData_Rendering_ReadPress(TCompressWaveData* this, int32_t* RFlg, int32_t* LFlg) {
|
||||
if (this->Hed.Channel == 2) {
|
||||
*RFlg = THuff_Read(this->RH); //STEREO
|
||||
*LFlg = THuff_Read(this->RH);
|
||||
}
|
||||
else {
|
||||
*RFlg = THuff_Read(this->RH); //MONO
|
||||
*LFlg = *RFlg;
|
||||
}
|
||||
}
|
||||
|
||||
void TCompressWaveData_Rendering_WriteWave(TCompressWaveData* this, int16_t** buf1, int32_t RVol, int32_t LVol) {
|
||||
TLRWRITEBUFFER bbb = {0};
|
||||
|
||||
if (this->Hed.Sample == 44100) { //44100 STEREO/MONO
|
||||
bbb.RBuf = RVol;
|
||||
bbb.LBuf = LVol;
|
||||
(*buf1)[0] = bbb.RBuf;
|
||||
(*buf1)[1] = bbb.LBuf;
|
||||
(*buf1) += 2;
|
||||
}
|
||||
if (this->Hed.Sample == 22050) { //22050 STEREO/MONO
|
||||
bbb.RBuf = (this->RBackBuf + RVol) / 2;
|
||||
bbb.LBuf = (this->LBackBuf + LVol) / 2;
|
||||
(*buf1)[0] = bbb.RBuf;
|
||||
(*buf1)[1] = bbb.LBuf;
|
||||
(*buf1) += 2;
|
||||
|
||||
bbb.RBuf = RVol;
|
||||
bbb.LBuf = LVol;
|
||||
(*buf1)[0] = bbb.RBuf;
|
||||
(*buf1)[1] = bbb.LBuf;
|
||||
(*buf1) += 2;
|
||||
|
||||
this->RBackBuf = RVol;
|
||||
this->LBackBuf = LVol;
|
||||
}
|
||||
}
|
||||
|
||||
int TCompressWaveData_Rendering(TCompressWaveData* this, int16_t* buf, uint32_t Len) {
|
||||
int result;
|
||||
int32_t RFlg, LFlg, RVol, LVol;
|
||||
int i, aaa;
|
||||
int16_t* buf1;
|
||||
int32_t PressLength, WaveStep;
|
||||
|
||||
|
||||
this->NowRendering = CW_TRUE;
|
||||
result = CW_FALSE;
|
||||
#if 0
|
||||
if (this->Data == NULL) {
|
||||
this->NowRendering = CW_FALSE;
|
||||
return result; //exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
//fadeout song stop
|
||||
if ((this->FVolume < 1) && (this->FSetVolume < 1)) {
|
||||
this->FPlay = CW_FALSE;
|
||||
}
|
||||
//if (abs(this->FSetVolume - this->FVolume) < this->Ffade) {
|
||||
// this->FPlay = CW_FALSE;
|
||||
//}
|
||||
|
||||
//stop if FPlay (play flag) wasn't set
|
||||
if (this->FPlay == CW_FALSE) {
|
||||
this->NowRendering = CW_FALSE;
|
||||
return result; //exit;
|
||||
}
|
||||
|
||||
//pre processing
|
||||
RVol = this->Fvv1;
|
||||
LVol = this->Fvv2;
|
||||
if (this->Hed.Sample == 44100)
|
||||
WaveStep = 4;
|
||||
else
|
||||
WaveStep = 8;
|
||||
|
||||
PressLength = (int32_t)Len / WaveStep;
|
||||
|
||||
//expansion processing
|
||||
buf1 = buf;
|
||||
for (i = 0; i < PressLength; i++) {
|
||||
|
||||
//crossed over?
|
||||
if (this->FWavePosition > this->FWaveLength) {
|
||||
if (this->FEndLoop == CW_TRUE) { //playback with loop?
|
||||
TCompressWaveData_Previous(this);
|
||||
}
|
||||
else { //in case of playback without loop
|
||||
this->FPlay = CW_FALSE;
|
||||
return result; //exit
|
||||
}
|
||||
}
|
||||
|
||||
//loop related
|
||||
if (this->Hed.LoopCount > this->FLoop) {
|
||||
//if position is loop start, hold current flag/state
|
||||
//shr 3 matches 8 bit aligment
|
||||
if ((this->Hed.LoopStart >> 3) == (this->FWavePosition >> 3)) {
|
||||
TCompressWaveData_GetLoopState(this);
|
||||
}
|
||||
//if reached loop end do loop.
|
||||
if ((this->Hed.LoopEnd >> 3) == (this->FWavePosition >> 3)) {
|
||||
if (this->Hed.LoopCount != 255)
|
||||
this->FLoop++;
|
||||
TCompressWaveData_SetLoopState(this);
|
||||
}
|
||||
}
|
||||
|
||||
//read
|
||||
TCompressWaveData_Rendering_ReadPress(this, &RFlg, &LFlg);
|
||||
this->Faa1 = this->Faa1 + this->Hed.Tbl[RFlg];
|
||||
this->Faa2 = this->Faa2 + this->Hed.Tbl[LFlg];
|
||||
this->Fvv1 = this->Fvv1 + this->Faa1;
|
||||
this->Fvv2 = this->Fvv2 + this->Faa2;
|
||||
|
||||
//volume adjustment
|
||||
aaa = this->FSetVolume - this->FVolume;
|
||||
if (abs(aaa) < this->Ffade) {
|
||||
this->FVolume = this->FSetVolume;
|
||||
}
|
||||
else {
|
||||
if (aaa > 0)
|
||||
this->FVolume = this->FVolume + this->Ffade;
|
||||
else
|
||||
this->FVolume = this->FVolume - this->Ffade;
|
||||
}
|
||||
|
||||
//threshold calcs (due to overflow)
|
||||
if (this->Fvv1 > +32760) {
|
||||
this->Fvv1 = +32760;
|
||||
this->Faa1 = 0;
|
||||
}
|
||||
if (this->Fvv1 < -32760) {
|
||||
this->Fvv1 = -32760;
|
||||
this->Faa1 = 0;
|
||||
}
|
||||
if (this->Fvv2 > +32760) {
|
||||
this->Fvv2 = +32760;
|
||||
this->Faa2 = 0;
|
||||
}
|
||||
if (this->Fvv2 < -32760) {
|
||||
this->Fvv2 = -32760;
|
||||
this->Faa2 = 0;
|
||||
}
|
||||
|
||||
aaa = (this->FVolume >> 20);
|
||||
RVol = this->Fvv1 * aaa / 256;
|
||||
LVol = this->Fvv2 * aaa / 256;
|
||||
|
||||
//expand to buffer
|
||||
TCompressWaveData_Rendering_WriteWave(this, &buf1, RVol, LVol);
|
||||
//advance playback position
|
||||
this->FWavePosition += WaveStep;
|
||||
}
|
||||
|
||||
//remainder calcs
|
||||
//depending on buffer lenght remainder may happen
|
||||
//example: 44100 / 4 = 11025...OK 44100 / 8 = 5512.5...NG
|
||||
// in that case appear as noise
|
||||
if (Len % 8 == 4) {
|
||||
TCompressWaveData_Rendering_WriteWave(this, &buf1, RVol, LVol);
|
||||
}
|
||||
|
||||
this->NowRendering = CW_FALSE;
|
||||
result = CW_TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//read compressed file from stream
|
||||
|
||||
static void TStream_Read_PRESSWAVEDATAHED(TStream* this, PRESSWAVEDATAHED* Hed) {
|
||||
uint8_t buf[0x538];
|
||||
int i, len;
|
||||
|
||||
read_streamfile(buf, this->Position, sizeof(buf), this->File);
|
||||
this->Position += sizeof(buf);
|
||||
|
||||
memcpy(Hed->HedChar, buf + 0x00, 8);
|
||||
Hed->Channel = get_u32le(buf + 0x08);
|
||||
Hed->Sample = get_u32le(buf + 0x0c);
|
||||
Hed->Bit = get_u32le(buf + 0x10);
|
||||
for (i = 0; i < 256; i++) {
|
||||
Hed->Tbl[i] = get_s32le(buf + 0x14 + i * 0x04);
|
||||
}
|
||||
Hed->UnPressSize = get_u64le(buf + 0x418);
|
||||
Hed->LoopStart = get_u64le(buf + 0x420);
|
||||
Hed->LoopEnd = get_u64le(buf + 0x428);
|
||||
Hed->LoopCount = get_u8 (buf + 0x430);
|
||||
|
||||
len = get_u8 (buf + 0x431);
|
||||
memcpy(Hed->MusicTitle, buf + 0x432, len);
|
||||
len = get_u8 (buf + 0x4B1);
|
||||
memcpy(Hed->MusicArtist, buf + 0x4B2, len);
|
||||
|
||||
/* 0x538: huffman table */
|
||||
/* 0x948: data start */
|
||||
}
|
||||
|
||||
int TCompressWaveData_LoadFromStream(TCompressWaveData* this, STREAMFILE* ss) {
|
||||
int result = CW_FALSE;
|
||||
TStream data = {0};
|
||||
|
||||
if (ss == NULL)
|
||||
return result;
|
||||
#if 0
|
||||
if (this->Data != NULL)
|
||||
TMemoryStream_Free(this->Data);
|
||||
#endif
|
||||
|
||||
data.File = ss; //data = TMemoryStream.Create;
|
||||
data.Size = get_streamfile_size(ss); //data.SetSize(ss.Size);
|
||||
//data.CopyFrom(ss,0);
|
||||
|
||||
//get header info
|
||||
data.Position = 0;
|
||||
|
||||
TStream_Read_PRESSWAVEDATAHED(&data, &this->Hed); //data.Read(Hed,sizeof(PRESSWAVEDATAHED));
|
||||
this->FWaveLength = this->Hed.UnPressSize;
|
||||
if (this->RH != NULL)
|
||||
THuff_Free(this->RH);
|
||||
this->RH = THuff_Create(&data);
|
||||
if (!this->RH) return result;
|
||||
|
||||
THuff_SetCipherCode(this->RH, 0x00);
|
||||
THuff_BeginRead(this->RH);
|
||||
|
||||
//initialize playback flag
|
||||
TCompressWaveData_Stop(this);
|
||||
TCompressWaveData_SetVolume(this, 1.0, 0.0);
|
||||
result = CW_TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//temp pause
|
||||
void TCompressWaveData_Pause(TCompressWaveData* this) {
|
||||
this->FPlay = CW_FALSE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//sets volume
|
||||
void TCompressWaveData_SetVolume(TCompressWaveData* this, float vol, float fade) {
|
||||
float aaa;
|
||||
|
||||
//EXTRA: C float seemingly can't store PW_MAXVOLUME (268435455 becomes 268435456.0), so must cast to double
|
||||
// to get proper results. Otherwise volume gets slightly different vs original (no casting needed there).
|
||||
// vol=1.0, fade=0.0 is the same as default params.
|
||||
|
||||
aaa = vol;
|
||||
//set volume threshold
|
||||
if (aaa > 1.0) aaa = 1.0;
|
||||
if (aaa < 0.0) aaa = 0.0;
|
||||
//calc volume increse
|
||||
if (fade < 0.01) { //with fade value
|
||||
this->Ffade = 0;
|
||||
this->FVolume = round(aaa * (double)PW_MAXVOLUME);
|
||||
this->FSetVolume = this->FVolume;
|
||||
}
|
||||
else { //without fade value
|
||||
this->Ffade = round(PW_MAXVOLUME / fade / 44100);
|
||||
this->FSetVolume = round(aaa * PW_MAXVOLUME);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns fade value
|
||||
float TCompressWaveData_GetFade(TCompressWaveData* this) {
|
||||
if ((this->Ffade == 0) || (abs(this->FVolume - this->FSetVolume) == 0)) {
|
||||
return 0; //exit;
|
||||
}
|
||||
return (abs(this->FVolume - this->FSetVolume)/44100) / this->Ffade;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns volume value
|
||||
float TCompressWaveData_GetVolume(TCompressWaveData* this) {
|
||||
return this->FVolume / PW_MAXVOLUME;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//returns volume after fade
|
||||
float TCompressWaveData_GetSetVolume(TCompressWaveData* this) {
|
||||
return this->FSetVolume / PW_MAXVOLUME;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//returns play time (current position). unit is secs
|
||||
float TCompressWaveData_GetPlayTime(TCompressWaveData* this) {
|
||||
return this->FWavePosition / (44100*4);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns song length. unit is secs
|
||||
float TCompressWaveData_GetTotalTime(TCompressWaveData* this) {
|
||||
return this->FWaveLength / (44100*4);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//play stop command. returns song to beginning
|
||||
void TCompressWaveData_Stop(TCompressWaveData* this) {
|
||||
//play flags to initial state
|
||||
this->FWavePosition = 0;
|
||||
this->Fvv1 = 0;
|
||||
this->Faa1 = 0;
|
||||
this->Fvv2 = 0;
|
||||
this->Faa2 = 0;
|
||||
this->LBackBuf = 0;
|
||||
this->RBackBuf = 0;
|
||||
TCompressWaveData_SetVolume(this, 1.0, 0);
|
||||
this->FPlay = CW_FALSE;
|
||||
this->FLoop = 0;
|
||||
#if 0
|
||||
if (this->Data == NULL)
|
||||
return;
|
||||
#endif
|
||||
|
||||
//EXTRA: presumably for threading but OG lib doesn't properly set this to false on all errors
|
||||
#if 0
|
||||
//sync
|
||||
while (this->NowRendering) {
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
//return to initial location
|
||||
THuff_MoveBeginPosition(this->RH);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns song to beginning. difference vs STOP is that fade isn't initialized
|
||||
void TCompressWaveData_Previous(TCompressWaveData* this) {
|
||||
//play flags to initial state
|
||||
this->FWavePosition = 0;
|
||||
this->Fvv1 = 0;
|
||||
this->Faa1 = 0;
|
||||
this->Fvv2 = 0;
|
||||
this->Faa2 = 0;
|
||||
this->LBackBuf = 0;
|
||||
this->RBackBuf = 0;
|
||||
this->FLoop = 0;
|
||||
|
||||
#if 0
|
||||
if (this->Data == NULL)
|
||||
return;
|
||||
#endif
|
||||
//return to initial location
|
||||
THuff_MoveBeginPosition(this->RH);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
//starts song playback
|
||||
void TCompressWaveData_Play(TCompressWaveData* this, int loop) {
|
||||
this->FPlay = CW_TRUE;
|
||||
this->FEndLoop = loop;
|
||||
if ((this->FVolume == 0) && (this->FSetVolume == 0))
|
||||
TCompressWaveData_SetVolume(this, 1.0,0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
//set parameters for looping
|
||||
//--------------------------------------------------------------
|
||||
//record encoded file position
|
||||
//since it uses huffman needs to held those flags too
|
||||
void TCompressWaveData_GetLoopState(TCompressWaveData* this) {
|
||||
this->LPFaa1 = this->Faa1;
|
||||
this->LPFaa2 = this->Faa2;
|
||||
this->LPFvv1 = this->Fvv1;
|
||||
this->LPFvv2 = this->Fvv2;
|
||||
THuff_GetPositionData(this->RH, &this->PosData);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
//return to recorded encoded file position
|
||||
void TCompressWaveData_SetLoopState(TCompressWaveData* this) {
|
||||
this->Faa1 = this->LPFaa1;
|
||||
this->Faa2 = this->LPFaa2;
|
||||
this->Fvv1 = this->LPFvv1;
|
||||
this->Fvv2 = this->LPFvv2;
|
||||
THuff_SetPositionData(this->RH, &this->PosData);
|
||||
this->FWavePosition = this->Hed.LoopStart;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//sets cipher code
|
||||
void TCompressWaveData_SetCipherCode(TCompressWaveData* this, uint32_t Num) {
|
||||
this->CipherCode = Num;
|
||||
}
|
||||
|
27
src/coding/compresswave_decode_lib.h
Normal file
27
src/coding/compresswave_decode_lib.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef _COMPRESSWAVE_DECODING_LIB_H
|
||||
#define _COMPRESSWAVE_DECODING_LIB_H
|
||||
#include "../streamfile.h"
|
||||
|
||||
typedef struct TCompressWaveData TCompressWaveData;
|
||||
|
||||
void TCompressWaveData_GetLoopState(TCompressWaveData* this);
|
||||
void TCompressWaveData_SetLoopState(TCompressWaveData* this);
|
||||
|
||||
TCompressWaveData* TCompressWaveData_Create();
|
||||
void TCompressWaveData_Free(TCompressWaveData* this);
|
||||
int TCompressWaveData_Rendering(TCompressWaveData* this, int16_t* buf, uint32_t Len);
|
||||
int TCompressWaveData_LoadFromStream(TCompressWaveData* this, STREAMFILE* ss);
|
||||
void TCompressWaveData_SetCipherCode(TCompressWaveData* this, uint32_t Num);
|
||||
|
||||
void TCompressWaveData_Play(TCompressWaveData* this, int loop);
|
||||
void TCompressWaveData_Stop(TCompressWaveData* this);
|
||||
void TCompressWaveData_Previous(TCompressWaveData* this);
|
||||
void TCompressWaveData_Pause(TCompressWaveData* this);
|
||||
void TCompressWaveData_SetVolume(TCompressWaveData* this, float vol, float fade);
|
||||
float TCompressWaveData_GetVolume(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetSetVolume(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetFade(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetPlayTime(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetTotalTime(TCompressWaveData* this);
|
||||
|
||||
#endif /*_COMPRESSWAVE_DECODING_LIB_H */
|
138
src/coding/compresswave_decoder.c
Normal file
138
src/coding/compresswave_decoder.c
Normal file
@ -0,0 +1,138 @@
|
||||
#include "coding.h"
|
||||
#include "coding_utils_samples.h"
|
||||
#include "compresswave_decode_lib.h"
|
||||
|
||||
|
||||
#define COMPRESSWAVE_MAX_FRAME_SAMPLES 0x1000 /* arbitrary but should be multiple of 2 for 22050 mode */
|
||||
|
||||
/* opaque struct */
|
||||
struct compresswave_codec_data {
|
||||
/* config */
|
||||
STREAMFILE* sf;
|
||||
TCompressWaveData* cw;
|
||||
|
||||
/* frame state */
|
||||
int16_t* samples;
|
||||
int frame_samples;
|
||||
|
||||
/* frame state */
|
||||
s16buf_t sbuf;
|
||||
int samples_discard;
|
||||
};
|
||||
|
||||
|
||||
compresswave_codec_data* init_compresswave(STREAMFILE* sf) {
|
||||
compresswave_codec_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(compresswave_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
if (!data->sf) goto fail;
|
||||
|
||||
data->frame_samples = COMPRESSWAVE_MAX_FRAME_SAMPLES;
|
||||
data->samples = malloc(2 * data->frame_samples * sizeof(int16_t)); /* always stereo */
|
||||
if (!data->samples) goto fail;
|
||||
|
||||
|
||||
data->cw = TCompressWaveData_Create();
|
||||
if (!data->cw) goto fail;
|
||||
|
||||
TCompressWaveData_LoadFromStream(data->cw, data->sf);
|
||||
|
||||
reset_compresswave(data);
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_compresswave(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int decode_frame(compresswave_codec_data* data, int32_t samples_to_do) {
|
||||
uint32_t Len;
|
||||
int ok;
|
||||
|
||||
data->sbuf.samples = data->samples;
|
||||
data->sbuf.channels = 2;
|
||||
data->sbuf.filled = 0;
|
||||
|
||||
if (samples_to_do > data->frame_samples)
|
||||
samples_to_do = data->frame_samples;
|
||||
if (samples_to_do % 2 && samples_to_do > 1)
|
||||
samples_to_do -= 1; /* 22khz does 2 samples at once */
|
||||
|
||||
Len = samples_to_do * sizeof(int16_t) * 2; /* forced stereo */
|
||||
|
||||
ok = TCompressWaveData_Rendering(data->cw, data->sbuf.samples, Len);
|
||||
if (!ok) goto fail;
|
||||
|
||||
data->sbuf.filled = samples_to_do;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
|
||||
int ok;
|
||||
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
s16buf_t* sbuf = &data->sbuf;
|
||||
|
||||
if (sbuf->filled <= 0) {
|
||||
ok = decode_frame(data, samples_to_do);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
if (data->samples_discard)
|
||||
s16buf_discard(&outbuf, sbuf, &data->samples_discard);
|
||||
else
|
||||
s16buf_consume(&outbuf, sbuf, &samples_to_do);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
VGM_LOG("COMPRESSWAVE: decode fail, missing %i samples\n", samples_to_do);
|
||||
s16buf_silence(&outbuf, &samples_to_do, 2);
|
||||
}
|
||||
|
||||
|
||||
void reset_compresswave(compresswave_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
/* actual way to reset internal flags */
|
||||
TCompressWaveData_Stop(data->cw);
|
||||
TCompressWaveData_Play(data->cw, 0);
|
||||
|
||||
data->sbuf.filled = 0;
|
||||
data->samples_discard = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void seek_compresswave(compresswave_codec_data* data, int32_t num_sample) {
|
||||
if (!data) return;
|
||||
|
||||
reset_compresswave(data);
|
||||
data->samples_discard += num_sample;
|
||||
}
|
||||
|
||||
void free_compresswave(compresswave_codec_data* data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
TCompressWaveData_Free(data->cw);
|
||||
|
||||
close_streamfile(data->sf);
|
||||
free(data->samples);
|
||||
free(data);
|
||||
}
|
||||
|
||||
STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data) {
|
||||
if (!data) return NULL;
|
||||
return data->sf;
|
||||
}
|
20
src/decode.c
20
src/decode.c
@ -40,6 +40,10 @@ void free_codec(VGMSTREAM* vgmstream) {
|
||||
free_imuse(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
free_compresswave(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
free_ea_mt(vgmstream->codec_data, vgmstream->channels);
|
||||
}
|
||||
@ -133,6 +137,10 @@ void seek_codec(VGMSTREAM* vgmstream) {
|
||||
seek_imuse(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
seek_compresswave(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
seek_ea_mt(vgmstream, vgmstream->loop_current_sample);
|
||||
}
|
||||
@ -231,6 +239,10 @@ void reset_codec(VGMSTREAM* vgmstream) {
|
||||
reset_imuse(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
reset_compresswave(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
reset_ea_mt(vgmstream);
|
||||
}
|
||||
@ -487,6 +499,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
||||
return 0; /* varies per mode */
|
||||
case coding_IMUSE:
|
||||
return 0; /* varies per frame */
|
||||
case coding_COMPRESSWAVE:
|
||||
return 0; /* multiple of 2 */
|
||||
case coding_EA_MT:
|
||||
return 0; /* 432, but variable in looped files */
|
||||
case coding_CIRCUS_VQ:
|
||||
@ -695,6 +709,8 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
||||
return 0; /* varies per mode? */
|
||||
case coding_IMUSE:
|
||||
return 0; /* varies per frame */
|
||||
case coding_COMPRESSWAVE:
|
||||
return 0; /* huffman bits */
|
||||
case coding_EA_MT:
|
||||
return 0; /* variable (frames of bit counts or PCM frames) */
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
@ -1406,6 +1422,10 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
||||
decode_imuse(vgmstream, buffer, samples_to_do);
|
||||
break;
|
||||
|
||||
case coding_COMPRESSWAVE:
|
||||
decode_compresswave(vgmstream->codec_data, buffer, samples_to_do);
|
||||
break;
|
||||
|
||||
case coding_EA_MT:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_ea_mt(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch);
|
||||
|
@ -138,6 +138,7 @@ static const char* extension_list[] = {
|
||||
"csa", //txth/reserved [LEGO Racers 2 (PS2)]
|
||||
"csmp",
|
||||
"cvs",
|
||||
"cwav",
|
||||
"cxs",
|
||||
|
||||
"da",
|
||||
@ -781,6 +782,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_OKI4S, "OKI 4-bit ADPCM (4-shift)"},
|
||||
{coding_PTADPCM, "Platinum 4-bit ADPCM"},
|
||||
{coding_IMUSE, "LucasArts iMUSE VIMA ADPCM"},
|
||||
{coding_COMPRESSWAVE, "CompressWave Huffman ADPCM"},
|
||||
|
||||
{coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
|
||||
{coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"},
|
||||
@ -1329,6 +1331,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_SBK, "Team17 SBK header"},
|
||||
{meta_DSP_WIIADPCM, "Exient WIIADPCM header"},
|
||||
{meta_DSP_CWAC, "CRI CWAC header"},
|
||||
{meta_COMPRESSWAVE, "CompressWave .cwav header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
@ -628,6 +628,10 @@
|
||||
RelativePath=".\meta\ck.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\compresswave.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\cpk.c"
|
||||
>
|
||||
@ -2030,6 +2034,10 @@
|
||||
RelativePath=".\coding\coding_utils_samples.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\compresswave_decoder_lib.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ea_mt_decoder_utk.h"
|
||||
>
|
||||
@ -2122,6 +2130,14 @@
|
||||
RelativePath=".\coding\coding_utils.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\compresswave_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\compresswave_decoder_lib.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\derf_decoder.c"
|
||||
>
|
||||
|
@ -155,6 +155,7 @@
|
||||
<ClInclude Include="coding\circus_decoder_miniz.h" />
|
||||
<ClInclude Include="coding\coding.h" />
|
||||
<ClInclude Include="coding\coding_utils_samples.h" />
|
||||
<ClInclude Include="coding\compresswave_decoder_lib.h" />
|
||||
<ClInclude Include="coding\ea_mt_decoder_utk.h" />
|
||||
<ClInclude Include="coding\g7221_decoder_aes.h" />
|
||||
<ClInclude Include="coding\g7221_decoder_lib.h" />
|
||||
@ -310,6 +311,7 @@
|
||||
<ClCompile Include="meta\bsf.c" />
|
||||
<ClCompile Include="meta\capdsp.c" />
|
||||
<ClCompile Include="meta\ck.c" />
|
||||
<ClCompile Include="meta\compresswave.c" />
|
||||
<ClCompile Include="meta\cpk.c" />
|
||||
<ClCompile Include="meta\cri_utf.c" />
|
||||
<ClCompile Include="meta\csb.c" />
|
||||
@ -594,6 +596,8 @@
|
||||
<ClCompile Include="coding\adx_decoder.c" />
|
||||
<ClCompile Include="coding\asf_decoder.c" />
|
||||
<ClCompile Include="coding\yamaha_decoder.c" />
|
||||
<ClCompile Include="coding\compresswave_decoder.c" />
|
||||
<ClCompile Include="coding\compresswave_decoder_lib.c" />
|
||||
<ClCompile Include="coding\derf_decoder.c" />
|
||||
<ClCompile Include="coding\dsa_decoder.c" />
|
||||
<ClCompile Include="coding\ea_mt_decoder.c" />
|
||||
|
@ -230,6 +230,9 @@
|
||||
<ClInclude Include="coding\coding_utils_samples.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\compresswave_decoder_lib.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\ea_mt_decoder_utk.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -436,6 +439,9 @@
|
||||
<ClCompile Include="meta\ck.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\compresswave.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\cpk.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1264,6 +1270,12 @@
|
||||
<ClCompile Include="coding\yamaha_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\compresswave_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\compresswave_decoder_lib.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\derf_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
50
src/meta/compresswave.c
Normal file
50
src/meta/compresswave.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* .CWAV - from CompressWave lib, found in few Japanese (doujin?) games around 1995-2002 [RADIO ZONDE (PC), GEO ~The Sword Millennia~ (PC)] */
|
||||
VGMSTREAM* init_vgmstream_compresswave(STREAMFILE *sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "cwav"))
|
||||
goto fail;
|
||||
|
||||
if (!is_id64be(0x00,sf, "CmpWave\0"))
|
||||
goto fail;
|
||||
|
||||
channels = 2; /* always, header channels is internal config */
|
||||
start_offset = 0x00;
|
||||
loop_flag = 1; //read_u8(0x430, sf) != 0; /* wrong count, see below */
|
||||
/* codec allows to use a cipher value, not seen */
|
||||
/* there is also title and artist, but default to "UnTitled" / "NoName" */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_COMPRESSWAVE;
|
||||
vgmstream->sample_rate = 44100; /* always, header rate is internal config */
|
||||
/* in PCM bytes */
|
||||
vgmstream->num_samples = read_u64le(0x418, sf) / sizeof(int16_t) / channels;
|
||||
/* known files have wrong loop values and just repeat */
|
||||
vgmstream->loop_start_sample = 0; //read_u64le(0x420, sf) / sizeof(int16_t) / channels;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples; //read_u64le(0x428, sf) / sizeof(int16_t) / channels;
|
||||
|
||||
vgmstream->codec_data = init_compresswave(sf);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_COMPRESSWAVE;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -936,4 +936,6 @@ VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_acx(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_compresswave(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -517,6 +517,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_dsp_cwac,
|
||||
init_vgmstream_ifs,
|
||||
init_vgmstream_acx,
|
||||
init_vgmstream_compresswave,
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||
@ -1333,6 +1334,10 @@ static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* v
|
||||
return acm_get_streamfile(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
return compresswave_get_streamfile(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
if (vgmstream->coding_type == coding_OGG_VORBIS) {
|
||||
return ogg_vorbis_get_streamfile(vgmstream->codec_data);
|
||||
|
@ -174,6 +174,7 @@ typedef enum {
|
||||
coding_OKI4S, /* OKI 4-bit ADPCM with 16-bit output and cuadruple step */
|
||||
coding_PTADPCM, /* Platinum 4-bit ADPCM */
|
||||
coding_IMUSE, /* LucasArts iMUSE Variable ADPCM */
|
||||
coding_COMPRESSWAVE, /* CompressWave Huffman ADPCM */
|
||||
|
||||
/* others */
|
||||
coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */
|
||||
@ -749,6 +750,7 @@ typedef enum {
|
||||
meta_SBK,
|
||||
meta_DSP_WIIADPCM,
|
||||
meta_DSP_CWAC,
|
||||
meta_COMPRESSWAVE,
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
|
Loading…
Reference in New Issue
Block a user