mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-07-04 13:47:04 -04:00
Added nioTSO's EA MicroTalk encoder to the project
This commit is contained in:
parent
4bee18c363
commit
41be518322
14 changed files with 2374 additions and 4 deletions
|
@ -1,10 +1,8 @@
|
|||
/** @file d20.h
|
||||
* @brief Implements Dungeons & Dragons style dice in C
|
||||
*
|
||||
* d20.h is a reimplementation of https://github.com/opensourcedoc/d20-c,
|
||||
* but following the principles of being a single header/file library with
|
||||
* a minimal API
|
||||
*
|
||||
* d20.h is a reimplementation of https://github.com/opensourcedoc/d20-c,but following the principles of being a single header/file library with a minimal API.
|
||||
* @source https://github.com/adamml/d20
|
||||
* @author adamml
|
||||
* @date 2022-11-07
|
||||
*/
|
||||
|
|
49
library/tools/utkencode/README.md
Normal file
49
library/tools/utkencode/README.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
## EA MicroTalk
|
||||
|
||||
EA MicroTalk (also UTalk or UTK) is a linear-predictive speech codec used in
|
||||
various games by Electronic Arts. The earliest known game to use it is
|
||||
Beasts & Bumpkins (1997). The codec has a bandwidth of 11.025kHz (sampling rate
|
||||
22.05kHz) and frame size of 20ms (432 samples) and only supports mono. It is
|
||||
typically encoded at 32 kbit/s.
|
||||
|
||||
Docs: http://wiki.niotso.org/UTK
|
||||
|
||||
In this repository, I have created a set of open source (public domain
|
||||
via the UNLICENSE) MicroTalk decoders/encoders.
|
||||
|
||||
* Use utkdecode to decode Maxis UTK (The Sims Online, SimCity 4).
|
||||
* Use utkdecode-bnb to decode PT/M10 (Beasts & Bumpkins).
|
||||
* Use utkdecode-fifa to decode FIFA 2001/2002 (PS2) speech samples. This tool
|
||||
supports regular MicroTalk and MicroTalk Revision 3
|
||||
[SCxl files](https://wiki.multimedia.cx/index.php/Electronic_Arts_SCxl).(*)
|
||||
* Use utkencode to encode Maxis UTK. (This is the simplest container format and
|
||||
is currently the only one supported for encoding.)
|
||||
|
||||
(*) I wasn't able to find any real-world MicroTalk Rev. 3 samples in any games.
|
||||
However, you can transcode a FIFA MicroTalk Rev. 2 file to Rev. 3 using
|
||||
[EA's Sound eXchange tool](https://wiki.multimedia.cx/index.php/Electronic_Arts_Sound_eXchange)
|
||||
(`sx -mt_blk input.dat -=output.dat`).
|
||||
|
||||
## Compiling
|
||||
|
||||
```
|
||||
gcc -Wall -Wextra -Wno-unused-function -ansi -pedantic -O2 -ffast-math -fwhole-program -g0 -s -static-libgcc -o utkdecode utkdecode.c
|
||||
gcc -Wall -Wextra -Wno-unused-function -ansi -pedantic -O2 -ffast-math -fwhole-program -g0 -s -static-libgcc -o utkdecode-fifa utkdecode-fifa.c
|
||||
gcc -Wall -Wextra -Wno-unused-function -ansi -pedantic -O2 -ffast-math -fwhole-program -g0 -s -static-libgcc -o utkdecode-bnb utkdecode-bnb.c
|
||||
gcc -Wall -Wextra -Wno-unused-function -ansi -pedantic -O2 -ffast-math -fwhole-program -g0 -s -static-libgcc -o utkencode utkencode.c
|
||||
```
|
||||
|
||||
## How the encoder works
|
||||
|
||||
The encoder for now is very simple. It does LPC analysis using the Levinson
|
||||
algorithm and transmits the entire excitation signal explicitly. Compression is
|
||||
achieved by choosing a large fixed codebook gain, such that each excitation
|
||||
sample has a large (coarse) quantization step size. Error is minimized in the
|
||||
excitation domain, and the quality is somewhat poor for bitrates below about
|
||||
48 kbit/s.
|
||||
|
||||
However, MicroTalk is a multi-pulse codec (it is cheap to code long runs of
|
||||
zeros in the excitation signal). Hence, a much better design (and indeed the
|
||||
standard practice for multi-pulse speech codecs) is to search for the positions
|
||||
and amplitudes of n pulses such that error is minimized in the output domain
|
||||
(or the perceptually weighted domain). This new encoder is still in the works.
|
24
library/tools/utkencode/UNLICENSE
Normal file
24
library/tools/utkencode/UNLICENSE
Normal file
|
@ -0,0 +1,24 @@
|
|||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
77
library/tools/utkencode/eachunk.h
Normal file
77
library/tools/utkencode/eachunk.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
typedef struct EAChunk {
|
||||
uint32_t type;
|
||||
uint8_t *start;
|
||||
uint8_t *ptr;
|
||||
uint8_t *end;
|
||||
} EAChunk;
|
||||
|
||||
static void chunk_read_bytes(EAChunk *chunk, uint8_t *dest, size_t size)
|
||||
{
|
||||
size_t bytes_remaining = chunk->end - chunk->ptr;
|
||||
|
||||
if (bytes_remaining < size) {
|
||||
fprintf(stderr, "error: unexpected end of chunk\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memcpy(dest, chunk->ptr, size);
|
||||
chunk->ptr += size;
|
||||
}
|
||||
|
||||
static uint32_t chunk_read_u32(EAChunk *chunk)
|
||||
{
|
||||
uint8_t dest[4];
|
||||
chunk_read_bytes(chunk, dest, sizeof(dest));
|
||||
return dest[0] | (dest[1] << 8) | (dest[2] << 16) | (dest[3] << 24);
|
||||
}
|
||||
|
||||
static uint32_t chunk_read_u8(EAChunk *chunk)
|
||||
{
|
||||
uint8_t dest;
|
||||
chunk_read_bytes(chunk, &dest, sizeof(dest));
|
||||
return dest;
|
||||
}
|
||||
|
||||
static uint32_t chunk_read_var_int(EAChunk *chunk)
|
||||
{
|
||||
uint8_t dest[4];
|
||||
uint8_t size = chunk_read_u8(chunk);
|
||||
|
||||
if (size > 4) {
|
||||
fprintf(stderr, "error: invalid varint size %u\n", (unsigned)size);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
chunk_read_bytes(chunk, dest, size);
|
||||
|
||||
/* read a big-endian integer of variable length */
|
||||
switch (size) {
|
||||
case 1: return dest[0];
|
||||
case 2: return (dest[0]<<8) | dest[1];
|
||||
case 3: return (dest[0]<<16) | (dest[1] << 8) | dest[2];
|
||||
case 4: return (dest[0]<<24) | (dest[1] << 16) | (dest[2] << 8) | dest[3];
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static EAChunk *read_chunk(FILE *fp)
|
||||
{
|
||||
uint32_t size;
|
||||
static EAChunk chunk;
|
||||
static uint8_t buffer[4096];
|
||||
|
||||
chunk.type = read_u32(fp);
|
||||
|
||||
size = read_u32(fp);
|
||||
if (size < 8 || size-8 > sizeof(buffer)) {
|
||||
fprintf(stderr, "error: invalid chunk size %u\n", (unsigned)size);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
size -= 8;
|
||||
read_bytes(fp, buffer, size);
|
||||
chunk.start = chunk.ptr = buffer;
|
||||
chunk.end = buffer+size;
|
||||
|
||||
return &chunk;
|
||||
}
|
78
library/tools/utkencode/io.h
Normal file
78
library/tools/utkencode/io.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
static void read_bytes(FILE *fp, uint8_t *dest, size_t size)
|
||||
{
|
||||
size_t bytes_copied;
|
||||
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
bytes_copied = fread(dest, 1, size, fp);
|
||||
if (bytes_copied < size) {
|
||||
if (ferror(fp))
|
||||
fprintf(stderr, "error: fread failed: %s\n", strerror(errno));
|
||||
else
|
||||
fprintf(stderr, "error: unexpected end of file\n");
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t read_u32(FILE *fp)
|
||||
{
|
||||
uint8_t dest[4];
|
||||
read_bytes(fp, dest, sizeof(dest));
|
||||
return dest[0] | (dest[1] << 8) | (dest[2] << 16) | (dest[3] << 24);
|
||||
}
|
||||
|
||||
static uint16_t read_u16(FILE *fp)
|
||||
{
|
||||
uint8_t dest[2];
|
||||
read_bytes(fp, dest, sizeof(dest));
|
||||
return dest[0] | (dest[1] << 8);
|
||||
}
|
||||
|
||||
static uint16_t read_u8(FILE *fp)
|
||||
{
|
||||
uint8_t dest;
|
||||
read_bytes(fp, &dest, sizeof(dest));
|
||||
return dest;
|
||||
}
|
||||
|
||||
static void write_bytes(FILE *fp, const uint8_t *dest, size_t size)
|
||||
{
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
if (fwrite(dest, 1, size, fp) != size) {
|
||||
fprintf(stderr, "error: fwrite failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_u32(FILE *fp, uint32_t x)
|
||||
{
|
||||
uint8_t dest[4];
|
||||
dest[0] = (uint8_t)x;
|
||||
dest[1] = (uint8_t)(x>>8);
|
||||
dest[2] = (uint8_t)(x>>16);
|
||||
dest[3] = (uint8_t)(x>>24);
|
||||
write_bytes(fp, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
static void write_u16(FILE *fp, uint16_t x)
|
||||
{
|
||||
uint8_t dest[2];
|
||||
dest[0] = (uint8_t)x;
|
||||
dest[1] = (uint8_t)(x>>8);
|
||||
write_bytes(fp, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
static void write_u8(FILE *fp, uint8_t x)
|
||||
{
|
||||
write_bytes(fp, &x, sizeof(x));
|
||||
}
|
BIN
library/tools/utkencode/samples/DS1.M10
Normal file
BIN
library/tools/utkencode/samples/DS1.M10
Normal file
Binary file not shown.
BIN
library/tools/utkencode/samples/fifa2001-mt5.dat
Normal file
BIN
library/tools/utkencode/samples/fifa2001-mt5.dat
Normal file
Binary file not shown.
BIN
library/tools/utkencode/samples/fifa2001.dat
Normal file
BIN
library/tools/utkencode/samples/fifa2001.dat
Normal file
Binary file not shown.
BIN
library/tools/utkencode/samples/male.utk
Normal file
BIN
library/tools/utkencode/samples/male.utk
Normal file
Binary file not shown.
446
library/tools/utkencode/utk.h
Normal file
446
library/tools/utkencode/utk.h
Normal file
|
@ -0,0 +1,446 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Note: This struct assumes a member alignment of 4 bytes.
|
||||
** This matters when pitch_lag > 216 on the first subframe of any given frame.
|
||||
*/
|
||||
typedef struct UTKContext {
|
||||
FILE *fp;
|
||||
const uint8_t *ptr, *end;
|
||||
int parsed_header;
|
||||
unsigned int bits_value;
|
||||
int bits_count;
|
||||
int reduced_bw;
|
||||
int multipulse_thresh;
|
||||
float fixed_gains[64];
|
||||
float rc[12];
|
||||
float synth_history[12];
|
||||
float adapt_cb[324];
|
||||
float decompressed_frame[432];
|
||||
} UTKContext;
|
||||
|
||||
enum { MDL_NORMAL = 0, MDL_LARGEPULSE = 1 };
|
||||
|
||||
static const float utk_rc_table[64] = {+0.0f,
|
||||
-.99677598476409912109375f,
|
||||
-.99032700061798095703125f,
|
||||
-.983879029750823974609375f,
|
||||
-.977430999279022216796875f,
|
||||
-.970982015132904052734375f,
|
||||
-.964533984661102294921875f,
|
||||
-.958085000514984130859375f,
|
||||
-.9516370296478271484375f,
|
||||
-.930754005908966064453125f,
|
||||
-.904959976673126220703125f,
|
||||
-.879167020320892333984375f,
|
||||
-.853372991085052490234375f,
|
||||
-.827579021453857421875f,
|
||||
-.801786005496978759765625f,
|
||||
-.775991976261138916015625f,
|
||||
-.75019800662994384765625f,
|
||||
-.724404990673065185546875f,
|
||||
-.6986110210418701171875f,
|
||||
-.6706349849700927734375f,
|
||||
-.61904799938201904296875f,
|
||||
-.567460000514984130859375f,
|
||||
-.515873014926910400390625f,
|
||||
-.4642859995365142822265625f,
|
||||
-.4126980006694793701171875f,
|
||||
-.361110985279083251953125f,
|
||||
-.309523999691009521484375f,
|
||||
-.257937014102935791015625f,
|
||||
-.20634900033473968505859375f,
|
||||
-.1547619998455047607421875f,
|
||||
-.10317499935626983642578125f,
|
||||
-.05158700048923492431640625f,
|
||||
+0.0f,
|
||||
+.05158700048923492431640625f,
|
||||
+.10317499935626983642578125f,
|
||||
+.1547619998455047607421875f,
|
||||
+.20634900033473968505859375f,
|
||||
+.257937014102935791015625f,
|
||||
+.309523999691009521484375f,
|
||||
+.361110985279083251953125f,
|
||||
+.4126980006694793701171875f,
|
||||
+.4642859995365142822265625f,
|
||||
+.515873014926910400390625f,
|
||||
+.567460000514984130859375f,
|
||||
+.61904799938201904296875f,
|
||||
+.6706349849700927734375f,
|
||||
+.6986110210418701171875f,
|
||||
+.724404990673065185546875f,
|
||||
+.75019800662994384765625f,
|
||||
+.775991976261138916015625f,
|
||||
+.801786005496978759765625f,
|
||||
+.827579021453857421875f,
|
||||
+.853372991085052490234375f,
|
||||
+.879167020320892333984375f,
|
||||
+.904959976673126220703125f,
|
||||
+.930754005908966064453125f,
|
||||
+.9516370296478271484375f,
|
||||
+.958085000514984130859375f,
|
||||
+.964533984661102294921875f,
|
||||
+.970982015132904052734375f,
|
||||
+.977430999279022216796875f,
|
||||
+.983879029750823974609375f,
|
||||
+.99032700061798095703125f,
|
||||
+.99677598476409912109375f};
|
||||
|
||||
static const uint8_t utk_codebooks[2][256] = {
|
||||
{/* normal model */
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17, 4, 6, 5, 9,
|
||||
4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21, 4, 6, 5, 9, 4, 6, 5, 13,
|
||||
4, 6, 5, 10, 4, 6, 5, 18, 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10,
|
||||
4, 6, 5, 25, 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22, 4, 6, 5, 9,
|
||||
4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18, 4, 6, 5, 9, 4, 6, 5, 14,
|
||||
4, 6, 5, 10, 4, 6, 5, 0, 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10,
|
||||
4, 6, 5, 17, 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
|
||||
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18, 4, 6, 5, 9,
|
||||
4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 26, 4, 6, 5, 9, 4, 6, 5, 13,
|
||||
4, 6, 5, 10, 4, 6, 5, 17, 4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10,
|
||||
4, 6, 5, 22, 4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
|
||||
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 2},
|
||||
{/* large-pulse model */
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23, 4, 11, 7, 15,
|
||||
4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27, 4, 11, 7, 15, 4, 12, 8, 19,
|
||||
4, 11, 7, 16, 4, 12, 8, 24, 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16,
|
||||
4, 12, 8, 1, 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28, 4, 11, 7, 15,
|
||||
4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24, 4, 11, 7, 15, 4, 12, 8, 20,
|
||||
4, 11, 7, 16, 4, 12, 8, 3, 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16,
|
||||
4, 12, 8, 23, 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
|
||||
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24, 4, 11, 7, 15,
|
||||
4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1, 4, 11, 7, 15, 4, 12, 8, 19,
|
||||
4, 11, 7, 16, 4, 12, 8, 23, 4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16,
|
||||
4, 12, 8, 28, 4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
|
||||
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3}};
|
||||
|
||||
static const struct {
|
||||
int next_model;
|
||||
int code_size;
|
||||
float pulse_value;
|
||||
} utk_commands[29] = {{MDL_LARGEPULSE, 8, 0.0f}, {MDL_LARGEPULSE, 7, 0.0f},
|
||||
{MDL_NORMAL, 8, 0.0f}, {MDL_NORMAL, 7, 0.0f},
|
||||
{MDL_NORMAL, 2, 0.0f}, {MDL_NORMAL, 2, -1.0f},
|
||||
{MDL_NORMAL, 2, +1.0f}, {MDL_NORMAL, 3, -1.0f},
|
||||
{MDL_NORMAL, 3, +1.0f}, {MDL_LARGEPULSE, 4, -2.0f},
|
||||
{MDL_LARGEPULSE, 4, +2.0f}, {MDL_LARGEPULSE, 3, -2.0f},
|
||||
{MDL_LARGEPULSE, 3, +2.0f}, {MDL_LARGEPULSE, 5, -3.0f},
|
||||
{MDL_LARGEPULSE, 5, +3.0f}, {MDL_LARGEPULSE, 4, -3.0f},
|
||||
{MDL_LARGEPULSE, 4, +3.0f}, {MDL_LARGEPULSE, 6, -4.0f},
|
||||
{MDL_LARGEPULSE, 6, +4.0f}, {MDL_LARGEPULSE, 5, -4.0f},
|
||||
{MDL_LARGEPULSE, 5, +4.0f}, {MDL_LARGEPULSE, 7, -5.0f},
|
||||
{MDL_LARGEPULSE, 7, +5.0f}, {MDL_LARGEPULSE, 6, -5.0f},
|
||||
{MDL_LARGEPULSE, 6, +5.0f}, {MDL_LARGEPULSE, 8, -6.0f},
|
||||
{MDL_LARGEPULSE, 8, +6.0f}, {MDL_LARGEPULSE, 7, -6.0f},
|
||||
{MDL_LARGEPULSE, 7, +6.0f}};
|
||||
|
||||
static int utk_read_byte(UTKContext *ctx) {
|
||||
if (ctx->ptr < ctx->end)
|
||||
return *ctx->ptr++;
|
||||
|
||||
if (ctx->fp) {
|
||||
static uint8_t buffer[4096];
|
||||
size_t bytes_copied = fread(buffer, 1, sizeof(buffer), ctx->fp);
|
||||
if (bytes_copied > 0 && bytes_copied <= sizeof(buffer)) {
|
||||
ctx->ptr = buffer;
|
||||
ctx->end = buffer + bytes_copied;
|
||||
return *ctx->ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int16_t utk_read_i16(UTKContext *ctx) {
|
||||
int x = utk_read_byte(ctx);
|
||||
x = (x << 8) | utk_read_byte(ctx);
|
||||
return x;
|
||||
}
|
||||
|
||||
static int utk_read_bits(UTKContext *ctx, int count) {
|
||||
int ret = ctx->bits_value & ((1 << count) - 1);
|
||||
ctx->bits_value >>= count;
|
||||
ctx->bits_count -= count;
|
||||
|
||||
if (ctx->bits_count < 8) {
|
||||
/* read another byte */
|
||||
ctx->bits_value |= utk_read_byte(ctx) << ctx->bits_count;
|
||||
ctx->bits_count += 8;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void utk_parse_header(UTKContext *ctx) {
|
||||
int i;
|
||||
float multiplier;
|
||||
|
||||
ctx->reduced_bw = utk_read_bits(ctx, 1);
|
||||
ctx->multipulse_thresh = 32 - utk_read_bits(ctx, 4);
|
||||
ctx->fixed_gains[0] = 8.0f * (1 + utk_read_bits(ctx, 4));
|
||||
multiplier = 1.04f + utk_read_bits(ctx, 6) * 0.001f;
|
||||
|
||||
for (i = 1; i < 64; i++)
|
||||
ctx->fixed_gains[i] = ctx->fixed_gains[i - 1] * multiplier;
|
||||
}
|
||||
|
||||
static void utk_decode_excitation(UTKContext *ctx, int use_multipulse,
|
||||
float *out, int stride) {
|
||||
int i;
|
||||
|
||||
if (use_multipulse) {
|
||||
/* multi-pulse model: n pulses are coded explicitly; the rest are zero */
|
||||
int model, cmd;
|
||||
model = 0;
|
||||
i = 0;
|
||||
while (i < 108) {
|
||||
cmd = utk_codebooks[model][ctx->bits_value & 0xff];
|
||||
model = utk_commands[cmd].next_model;
|
||||
utk_read_bits(ctx, utk_commands[cmd].code_size);
|
||||
|
||||
if (cmd > 3) {
|
||||
/* insert a pulse with magnitude <= 6.0f */
|
||||
out[i] = utk_commands[cmd].pulse_value;
|
||||
i += stride;
|
||||
} else if (cmd > 1) {
|
||||
/* insert between 7 and 70 zeros */
|
||||
int count = 7 + utk_read_bits(ctx, 6);
|
||||
if (i + count * stride > 108)
|
||||
count = (108 - i) / stride;
|
||||
|
||||
while (count > 0) {
|
||||
out[i] = 0.0f;
|
||||
i += stride;
|
||||
count--;
|
||||
}
|
||||
} else {
|
||||
/* insert a pulse with magnitude >= 7.0f */
|
||||
int x = 7;
|
||||
|
||||
while (utk_read_bits(ctx, 1))
|
||||
x++;
|
||||
|
||||
if (!utk_read_bits(ctx, 1))
|
||||
x *= -1;
|
||||
|
||||
out[i] = (float)x;
|
||||
i += stride;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* RELP model: entire residual (excitation) signal is coded explicitly */
|
||||
i = 0;
|
||||
while (i < 108) {
|
||||
if (!utk_read_bits(ctx, 1))
|
||||
out[i] = 0.0f;
|
||||
else if (!utk_read_bits(ctx, 1))
|
||||
out[i] = -2.0f;
|
||||
else
|
||||
out[i] = 2.0f;
|
||||
|
||||
i += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_to_lpc(const float *rc, float *lpc) {
|
||||
int i, j;
|
||||
float tmp1[12];
|
||||
float tmp2[12];
|
||||
|
||||
for (i = 10; i >= 0; i--)
|
||||
tmp2[1 + i] = rc[i];
|
||||
|
||||
tmp2[0] = 1.0f;
|
||||
|
||||
for (i = 0; i < 12; i++) {
|
||||
float x = -tmp2[11] * rc[11];
|
||||
|
||||
for (j = 10; j >= 0; j--) {
|
||||
x -= tmp2[j] * rc[j];
|
||||
tmp2[j + 1] = x * rc[j] + tmp2[j];
|
||||
}
|
||||
|
||||
tmp1[i] = tmp2[0] = x;
|
||||
|
||||
for (j = 0; j < i; j++)
|
||||
x -= tmp1[i - 1 - j] * lpc[j];
|
||||
|
||||
lpc[i] = x;
|
||||
}
|
||||
}
|
||||
|
||||
static void utk_lp_synthesis_filter(UTKContext *ctx, int offset,
|
||||
int num_blocks) {
|
||||
int i, j, k;
|
||||
float lpc[12];
|
||||
float *ptr = &ctx->decompressed_frame[offset];
|
||||
|
||||
rc_to_lpc(ctx->rc, lpc);
|
||||
|
||||
for (i = 0; i < num_blocks; i++) {
|
||||
for (j = 0; j < 12; j++) {
|
||||
float x = *ptr;
|
||||
|
||||
for (k = 0; k < j; k++)
|
||||
x += lpc[k] * ctx->synth_history[k - j + 12];
|
||||
for (; k < 12; k++)
|
||||
x += lpc[k] * ctx->synth_history[k - j];
|
||||
|
||||
ctx->synth_history[11 - j] = x;
|
||||
*ptr++ = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Public functions.
|
||||
*/
|
||||
|
||||
static void utk_decode_frame(UTKContext *ctx) {
|
||||
int i, j;
|
||||
int use_multipulse = 0;
|
||||
float excitation[5 + 108 + 5];
|
||||
float rc_delta[12];
|
||||
|
||||
if (!ctx->bits_count) {
|
||||
ctx->bits_value = utk_read_byte(ctx);
|
||||
ctx->bits_count = 8;
|
||||
}
|
||||
|
||||
if (!ctx->parsed_header) {
|
||||
utk_parse_header(ctx);
|
||||
ctx->parsed_header = 1;
|
||||
}
|
||||
|
||||
memset(&excitation[0], 0, 5 * sizeof(float));
|
||||
memset(&excitation[5 + 108], 0, 5 * sizeof(float));
|
||||
|
||||
/* read the reflection coefficients */
|
||||
for (i = 0; i < 12; i++) {
|
||||
int idx;
|
||||
if (i == 0) {
|
||||
idx = utk_read_bits(ctx, 6);
|
||||
if (idx < ctx->multipulse_thresh)
|
||||
use_multipulse = 1;
|
||||
} else if (i < 4) {
|
||||
idx = utk_read_bits(ctx, 6);
|
||||
} else {
|
||||
idx = 16 + utk_read_bits(ctx, 5);
|
||||
}
|
||||
|
||||
rc_delta[i] = (utk_rc_table[idx] - ctx->rc[i]) * 0.25f;
|
||||
}
|
||||
|
||||
/* decode four subframes */
|
||||
for (i = 0; i < 4; i++) {
|
||||
int pitch_lag = utk_read_bits(ctx, 8);
|
||||
float pitch_gain = (float)utk_read_bits(ctx, 4) / 15.0f;
|
||||
float fixed_gain = ctx->fixed_gains[utk_read_bits(ctx, 6)];
|
||||
|
||||
if (!ctx->reduced_bw) {
|
||||
utk_decode_excitation(ctx, use_multipulse, &excitation[5], 1);
|
||||
} else {
|
||||
/* residual (excitation) signal is encoded at reduced bandwidth */
|
||||
int align = utk_read_bits(ctx, 1);
|
||||
int zero = utk_read_bits(ctx, 1);
|
||||
|
||||
utk_decode_excitation(ctx, use_multipulse, &excitation[5 + align], 2);
|
||||
|
||||
if (zero) {
|
||||
/* fill the remaining samples with zero
|
||||
** (spectrum is duplicated into high frequencies) */
|
||||
for (j = 0; j < 54; j++)
|
||||
excitation[5 + (1 - align) + 2 * j] = 0.0f;
|
||||
} else {
|
||||
/* interpolate the remaining samples
|
||||
** (spectrum is low-pass filtered) */
|
||||
float *ptr = &excitation[5 + (1 - align)];
|
||||
for (j = 0; j < 108; j += 2)
|
||||
ptr[j] = ptr[j - 5] * 0.01803267933428287506103515625f -
|
||||
ptr[j - 3] * 0.114591561257839202880859375f +
|
||||
ptr[j - 1] * 0.597385942935943603515625f +
|
||||
ptr[j + 1] * 0.597385942935943603515625f -
|
||||
ptr[j + 3] * 0.114591561257839202880859375f +
|
||||
ptr[j + 5] * 0.01803267933428287506103515625f;
|
||||
|
||||
/* scale by 0.5f to give the sinc impulse response unit energy */
|
||||
fixed_gain *= 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < 108; j++)
|
||||
ctx->decompressed_frame[108 * i + j] =
|
||||
fixed_gain * excitation[5 + j] +
|
||||
pitch_gain * ctx->adapt_cb[108 * i + 216 - pitch_lag + j];
|
||||
}
|
||||
|
||||
for (i = 0; i < 324; i++)
|
||||
ctx->adapt_cb[i] = ctx->decompressed_frame[108 + i];
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 12; j++)
|
||||
ctx->rc[j] += rc_delta[j];
|
||||
|
||||
utk_lp_synthesis_filter(ctx, 12 * i, i < 3 ? 1 : 33);
|
||||
}
|
||||
}
|
||||
|
||||
static void utk_init(UTKContext *ctx) { memset(ctx, 0, sizeof(*ctx)); }
|
||||
|
||||
static void utk_set_fp(UTKContext *ctx, FILE *fp) {
|
||||
ctx->fp = fp;
|
||||
|
||||
/* reset the bit reader */
|
||||
ctx->bits_count = 0;
|
||||
}
|
||||
|
||||
static void utk_set_ptr(UTKContext *ctx, const uint8_t *ptr,
|
||||
const uint8_t *end) {
|
||||
ctx->ptr = ptr;
|
||||
ctx->end = end;
|
||||
|
||||
/* reset the bit reader */
|
||||
ctx->bits_count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** MicroTalk Revision 3 decoding function.
|
||||
*/
|
||||
|
||||
static void utk_rev3_decode_frame(UTKContext *ctx) {
|
||||
int pcm_data_present = (utk_read_byte(ctx) == 0xee);
|
||||
int i;
|
||||
|
||||
utk_decode_frame(ctx);
|
||||
|
||||
/* unread the last 8 bits and reset the bit reader */
|
||||
ctx->ptr--;
|
||||
ctx->bits_count = 0;
|
||||
|
||||
if (pcm_data_present) {
|
||||
/* Overwrite n samples at a given offset in the decoded frame with
|
||||
** raw PCM data. */
|
||||
int offset = utk_read_i16(ctx);
|
||||
int count = utk_read_i16(ctx);
|
||||
|
||||
/* sx.exe does not do any bounds checking or clamping of these two
|
||||
** fields (see 004274D1 in sx.exe v3.01.01), which means a specially
|
||||
** crafted MT5:1 file can crash sx.exe.
|
||||
** We will throw an error instead. */
|
||||
if (offset < 0 || offset > 432) {
|
||||
fprintf(stderr, "error: invalid PCM offset %d\n", offset);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (count < 0 || count > 432 - offset) {
|
||||
fprintf(stderr, "error: invalid PCM count %d\n", count);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
ctx->decompressed_frame[offset + i] = (float)utk_read_i16(ctx);
|
||||
}
|
||||
}
|
167
library/tools/utkencode/utkdecode-bnb.c
Normal file
167
library/tools/utkencode/utkdecode-bnb.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
** utkdecode-bnb
|
||||
** Decode Beasts & Bumpkins M10 to wav.
|
||||
** Authors: Andrew D'Addesio
|
||||
** License: Public domain
|
||||
** Compile: gcc -Wall -Wextra -Wno-unused-function -ansi -pedantic -O2 -ffast-math
|
||||
** -fwhole-program -g0 -s -o utkdecode-bnb utkdecode-bnb.c
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "utk.h"
|
||||
#include "io.h"
|
||||
#include "eachunk.h"
|
||||
|
||||
#define MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
|
||||
#define ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f))
|
||||
#define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#define MAX(x,y) ((x)>(y)?(x):(y))
|
||||
#define CLAMP(x,min,max) MIN(MAX(x,min),max)
|
||||
|
||||
typedef struct PTContext {
|
||||
FILE *infp, *outfp;
|
||||
uint32_t num_samples;
|
||||
uint32_t compression_type;
|
||||
UTKContext utk;
|
||||
} PTContext;
|
||||
|
||||
static void pt_read_header(PTContext *pt)
|
||||
{
|
||||
EAChunk *chunk = read_chunk(pt->infp);
|
||||
|
||||
if ((chunk->type & 0xffff) != MAKE_U32('P','T','\x00','\x00')) {
|
||||
fprintf(stderr, "error: expected PT chunk\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
uint8_t cmd = chunk_read_u8(chunk);
|
||||
if (cmd == 0xFD) {
|
||||
while (1) {
|
||||
uint8_t key = chunk_read_u8(chunk);
|
||||
uint32_t value = chunk_read_var_int(chunk);
|
||||
|
||||
if (key == 0xFF)
|
||||
break;
|
||||
else if (key == 0x85)
|
||||
pt->num_samples = value;
|
||||
else if (key == 0x83)
|
||||
pt->compression_type = value;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
chunk_read_var_int(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if (pt->compression_type != 9) {
|
||||
fprintf(stderr, "error: invalid compression type %u (expected 9 for MicroTalk 10:1)\n",
|
||||
(unsigned)pt->compression_type);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (pt->num_samples >= 0x01000000) {
|
||||
fprintf(stderr, "error: invalid num_samples %u\n", pt->num_samples);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Initialize the decoder. */
|
||||
utk_init(&pt->utk);
|
||||
|
||||
/* Write the WAV header. */
|
||||
write_u32(pt->outfp, MAKE_U32('R','I','F','F'));
|
||||
write_u32(pt->outfp, 36 + pt->num_samples*2);
|
||||
write_u32(pt->outfp, MAKE_U32('W','A','V','E'));
|
||||
write_u32(pt->outfp, MAKE_U32('f','m','t',' '));
|
||||
write_u32(pt->outfp, 16);
|
||||
write_u16(pt->outfp, 1);
|
||||
write_u16(pt->outfp, 1);
|
||||
write_u32(pt->outfp, 22050);
|
||||
write_u32(pt->outfp, 22050*2);
|
||||
write_u16(pt->outfp, 2);
|
||||
write_u16(pt->outfp, 16);
|
||||
write_u32(pt->outfp, MAKE_U32('d','a','t','a'));
|
||||
write_u32(pt->outfp, pt->num_samples*2);
|
||||
}
|
||||
|
||||
static void pt_decode(PTContext *pt)
|
||||
{
|
||||
UTKContext *utk = &pt->utk;
|
||||
uint32_t num_samples = pt->num_samples;
|
||||
|
||||
utk_set_fp(utk, pt->infp);
|
||||
|
||||
while (num_samples > 0) {
|
||||
int count = MIN(num_samples, 432);
|
||||
int i;
|
||||
|
||||
utk_decode_frame(utk);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
int x = ROUND(pt->utk.decompressed_frame[i]);
|
||||
write_u16(pt->outfp, (int16_t)CLAMP(x, -32768, 32767));
|
||||
}
|
||||
|
||||
num_samples -= count;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
PTContext pt;
|
||||
const char *infile, *outfile;
|
||||
FILE *infp, *outfp;
|
||||
int force = 0;
|
||||
|
||||
/* Parse arguments. */
|
||||
if (argc == 4 && !strcmp(argv[1], "-f")) {
|
||||
force = 1;
|
||||
argv++, argc--;
|
||||
}
|
||||
|
||||
if (argc != 3) {
|
||||
printf("Usage: utkdecode-bnb [-f] infile outfile\n");
|
||||
printf("Decode Beasts & Bumpkins M10 to wav.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile = argv[1];
|
||||
outfile = argv[2];
|
||||
|
||||
/* Open the input/output files. */
|
||||
infp = fopen(infile, "rb");
|
||||
if (!infp) {
|
||||
fprintf(stderr, "error: failed to open '%s' for reading: %s\n", infile, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!force && fopen(outfile, "rb")) {
|
||||
fprintf(stderr, "error: '%s' already exists\n", outfile);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outfp = fopen(outfile, "wb");
|
||||
if (!outfp) {
|
||||
fprintf(stderr, "error: failed to create '%s': %s\n", outfile, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.infp = infp;
|
||||
pt.outfp = outfp;
|
||||
|
||||
pt_read_header(&pt);
|
||||
pt_decode(&pt);
|
||||
|
||||
if (fclose(outfp) != 0) {
|
||||
fprintf(stderr, "error: failed to close '%s': %s\n", outfile, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fclose(infp);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
229
library/tools/utkencode/utkdecode-fifa.c
Normal file
229
library/tools/utkencode/utkdecode-fifa.c
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
** utkdecode-fifa
|
||||
** Decode FIFA 2001/2002 MicroTalk to wav.
|
||||
** Authors: Andrew D'Addesio
|
||||
** License: Public domain
|
||||
** Compile: gcc -Wall -Wextra -Wno-unused-function -ansi -pedantic -O2 -ffast-math
|
||||
** -fwhole-program -g0 -s -o utkdecode-fifa utkdecode-fifa.c
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "utk.h"
|
||||
#include "io.h"
|
||||
#include "eachunk.h"
|
||||
|
||||
#define MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
|
||||
#define ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f))
|
||||
#define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#define MAX(x,y) ((x)>(y)?(x):(y))
|
||||
#define CLAMP(x,min,max) MIN(MAX(x,min),max)
|
||||
|
||||
typedef struct EAContext {
|
||||
FILE *infp, *outfp;
|
||||
uint32_t audio_pos;
|
||||
uint32_t num_samples;
|
||||
uint32_t num_data_chunks;
|
||||
uint32_t compression_type;
|
||||
uint32_t codec_revision;
|
||||
UTKContext utk;
|
||||
} EAContext;
|
||||
|
||||
static void ea_read_schl(EAContext *ea)
|
||||
{
|
||||
uint32_t id;
|
||||
EAChunk *chunk = read_chunk(ea->infp);
|
||||
|
||||
if (chunk->type != MAKE_U32('S','C','H','l')) {
|
||||
fprintf(stderr, "error: expected SCHl chunk\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
id = chunk_read_u32(chunk);
|
||||
if ((id & 0xffff) != MAKE_U32('P','T','\x00','\x00')) {
|
||||
fprintf(stderr, "error: expected PT chunk in SCHl header\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
uint8_t cmd = chunk_read_u8(chunk);
|
||||
if (cmd == 0xFD) {
|
||||
while (1) {
|
||||
uint8_t key = chunk_read_u8(chunk);
|
||||
uint32_t value = chunk_read_var_int(chunk);
|
||||
|
||||
if (key == 0xFF)
|
||||
break;
|
||||
else if (key == 0x80)
|
||||
ea->codec_revision = value;
|
||||
else if (key == 0x85)
|
||||
ea->num_samples = value;
|
||||
else if (key == 0xA0)
|
||||
ea->compression_type = value;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
chunk_read_var_int(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if (ea->compression_type != 4 && ea->compression_type != 22) {
|
||||
fprintf(stderr, "error: invalid compression type %u (expected 4 for MicroTalk 10:1 or 22 for MicroTalk 5:1)\n",
|
||||
(unsigned)ea->compression_type);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (ea->num_samples >= 0x01000000) {
|
||||
fprintf(stderr, "error: invalid num_samples %u\n", ea->num_samples);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Initialize the decoder. */
|
||||
utk_init(&ea->utk);
|
||||
|
||||
/* Write the WAV header. */
|
||||
write_u32(ea->outfp, MAKE_U32('R','I','F','F'));
|
||||
write_u32(ea->outfp, 36 + ea->num_samples*2);
|
||||
write_u32(ea->outfp, MAKE_U32('W','A','V','E'));
|
||||
write_u32(ea->outfp, MAKE_U32('f','m','t',' '));
|
||||
write_u32(ea->outfp, 16);
|
||||
write_u16(ea->outfp, 1);
|
||||
write_u16(ea->outfp, 1);
|
||||
write_u32(ea->outfp, 22050);
|
||||
write_u32(ea->outfp, 22050*2);
|
||||
write_u16(ea->outfp, 2);
|
||||
write_u16(ea->outfp, 16);
|
||||
write_u32(ea->outfp, MAKE_U32('d','a','t','a'));
|
||||
write_u32(ea->outfp, ea->num_samples*2);
|
||||
}
|
||||
|
||||
static void ea_read_sccl(EAContext *ea)
|
||||
{
|
||||
EAChunk *chunk = read_chunk(ea->infp);
|
||||
|
||||
if (chunk->type != MAKE_U32('S','C','C','l')) {
|
||||
fprintf(stderr, "error: expected SCCl chunk\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ea->num_data_chunks = chunk_read_u32(chunk);
|
||||
if (ea->num_data_chunks >= 0x01000000) {
|
||||
fprintf(stderr, "error: invalid num_data_chunks %u\n", (unsigned)ea->num_data_chunks);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void ea_read_scdl(EAContext *ea)
|
||||
{
|
||||
EAChunk *chunk = read_chunk(ea->infp);
|
||||
UTKContext *utk = &ea->utk;
|
||||
uint32_t num_samples;
|
||||
|
||||
if (chunk->type != MAKE_U32('S','C','D','l')) {
|
||||
fprintf(stderr, "error: expected SCDl chunk\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
num_samples = chunk_read_u32(chunk);
|
||||
chunk_read_u32(chunk); /* unknown */
|
||||
chunk_read_u8(chunk); /* unknown */
|
||||
|
||||
if (num_samples > ea->num_samples - ea->audio_pos)
|
||||
num_samples = ea->num_samples - ea->audio_pos;
|
||||
|
||||
utk_set_ptr(utk, chunk->ptr, chunk->end);
|
||||
|
||||
while (num_samples > 0) {
|
||||
int count = MIN(num_samples, 432);
|
||||
int i;
|
||||
|
||||
if (ea->codec_revision >= 3)
|
||||
utk_rev3_decode_frame(utk);
|
||||
else
|
||||
utk_decode_frame(utk);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
int x = ROUND(ea->utk.decompressed_frame[i]);
|
||||
write_u16(ea->outfp, (int16_t)CLAMP(x, -32768, 32767));
|
||||
}
|
||||
|
||||
ea->audio_pos += count;
|
||||
num_samples -= count;
|
||||
}
|
||||
}
|
||||
|
||||
static void ea_read_scel(const EAContext *ea)
|
||||
{
|
||||
EAChunk *chunk = read_chunk(ea->infp);
|
||||
|
||||
if (chunk->type != MAKE_U32('S','C','E','l')) {
|
||||
fprintf(stderr, "error: expected SCEl chunk\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (ea->audio_pos != ea->num_samples) {
|
||||
fprintf(stderr, "error: failed to decode the correct number of samples\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
EAContext ea;
|
||||
const char *infile, *outfile;
|
||||
FILE *infp, *outfp;
|
||||
int force = 0;
|
||||
unsigned int i;
|
||||
|
||||
if (argc == 4 && !strcmp(argv[1], "-f")) {
|
||||
force = 1;
|
||||
argv++, argc--;
|
||||
}
|
||||
|
||||
if (argc != 3) {
|
||||
printf("Usage: utkdecode-fifa [-f] infile outfile\n");
|
||||
printf("Decode FIFA 2001/2002 MicroTalk to wav.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile = argv[1];
|
||||
outfile = argv[2];
|
||||
|
||||
infp = fopen(infile, "rb");
|
||||
if (!infp) {
|
||||
fprintf(stderr, "error: failed to open '%s' for reading: %s\n", infile, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!force && fopen(outfile, "rb")) {
|
||||
fprintf(stderr, "error: '%s' already exists\n", outfile);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outfp = fopen(outfile, "wb");
|
||||
if (!outfp) {
|
||||
fprintf(stderr, "error: failed to create '%s': %s\n", outfile, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
memset(&ea, 0, sizeof(ea));
|
||||
ea.infp = infp;
|
||||
ea.outfp = outfp;
|
||||
|
||||
ea_read_schl(&ea);
|
||||
ea_read_sccl(&ea);
|
||||
|
||||
for (i = 0; i < ea.num_data_chunks; i++)
|
||||
ea_read_scdl(&ea);
|
||||
|
||||
ea_read_scel(&ea);
|
||||
|
||||
if (!outfp) {
|
||||
fprintf(stderr, "error: failed to close '%s': %s\n", outfile, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
172
library/tools/utkencode/utkdecode.c
Normal file
172
library/tools/utkencode/utkdecode.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
** utkdecode
|
||||
** Decode Maxis UTK to wav.
|
||||
** Authors: Andrew D'Addesio
|
||||
** License: Public domain
|
||||
** Compile: gcc -Wall -Wextra -Wno-unused-function -ansi -pedantic -O2 -ffast-math
|
||||
** -fwhole-program -g0 -s -o utkdecode utkdecode.c
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "utk.h"
|
||||
#include "io.h"
|
||||
|
||||
#define MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
|
||||
#define ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f))
|
||||
#define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#define MAX(x,y) ((x)>(y)?(x):(y))
|
||||
#define CLAMP(x,min,max) MIN(MAX(x,min),max)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *infile, *outfile;
|
||||
UTKContext ctx;
|
||||
uint32_t sID;
|
||||
uint32_t dwOutSize;
|
||||
uint32_t dwWfxSize;
|
||||
uint16_t wFormatTag;
|
||||
uint16_t nChannels;
|
||||
uint32_t nSamplesPerSec;
|
||||
uint32_t nAvgBytesPerSec;
|
||||
uint16_t nBlockAlign;
|
||||
uint16_t wBitsPerSample;
|
||||
uint16_t cbSize;
|
||||
uint32_t num_samples;
|
||||
FILE *infp, *outfp;
|
||||
int force = 0;
|
||||
int error = 0;
|
||||
int i;
|
||||
|
||||
/* Parse arguments. */
|
||||
if (argc == 4 && !strcmp(argv[1], "-f")) {
|
||||
force = 1;
|
||||
argv++, argc--;
|
||||
}
|
||||
|
||||
if (argc != 3) {
|
||||
printf("Usage: utkdecode [-f] infile outfile\n");
|
||||
printf("Decode Maxis UTK to wav.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile = argv[1];
|
||||
outfile = argv[2];
|
||||
|
||||
/* Open the input/output files. */
|
||||
infp = fopen(infile, "rb");
|
||||
if (!infp) {
|
||||
fprintf(stderr, "error: failed to open '%s' for reading: %s\n", infile, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!force && fopen(outfile, "rb")) {
|
||||
fprintf(stderr, "error: '%s' already exists\n", outfile);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outfp = fopen(outfile, "wb");
|
||||
if (!outfp) {
|
||||
fprintf(stderr, "error: failed to create '%s': %s\n", outfile, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Parse the UTK header. */
|
||||
sID = read_u32(infp);
|
||||
dwOutSize = read_u32(infp);
|
||||
dwWfxSize = read_u32(infp);
|
||||
wFormatTag = read_u16(infp);
|
||||
nChannels = read_u16(infp);
|
||||
nSamplesPerSec = read_u32(infp);
|
||||
nAvgBytesPerSec = read_u32(infp);
|
||||
nBlockAlign = read_u16(infp);
|
||||
wBitsPerSample = read_u16(infp);
|
||||
cbSize = read_u16(infp);
|
||||
read_u16(infp); /* padding */
|
||||
|
||||
if (sID != MAKE_U32('U','T','M','0')) {
|
||||
fprintf(stderr, "error: not a valid UTK file (expected UTM0 signature)\n");
|
||||
return EXIT_FAILURE;
|
||||
} else if ((dwOutSize & 0x01) != 0 || dwOutSize >= 0x01000000) {
|
||||
fprintf(stderr, "error: invalid dwOutSize %u\n", (unsigned)dwOutSize);
|
||||
return EXIT_FAILURE;
|
||||
} else if (dwWfxSize != 20) {
|
||||
fprintf(stderr, "error: invalid dwWfxSize %u (expected 20)\n", (unsigned)dwWfxSize);
|
||||
return EXIT_FAILURE;
|
||||
} else if (wFormatTag != 1) {
|
||||
fprintf(stderr, "error: invalid wFormatTag %u (expected 1)\n", (unsigned)wFormatTag);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (nChannels != 1) {
|
||||
fprintf(stderr, "error: invalid nChannels %u (only mono is supported)\n", (unsigned)nChannels);
|
||||
error = 1;
|
||||
}
|
||||
if (nSamplesPerSec < 8000 || nSamplesPerSec > 192000) {
|
||||
fprintf(stderr, "error: invalid nSamplesPerSec %u\n", (unsigned)nSamplesPerSec);
|
||||
error = 1;
|
||||
}
|
||||
if (nAvgBytesPerSec != nSamplesPerSec * nBlockAlign) {
|
||||
fprintf(stderr, "error: invalid nAvgBytesPerSec %u (expected nSamplesPerSec * nBlockAlign)\n", (unsigned)nAvgBytesPerSec);
|
||||
error = 1;
|
||||
}
|
||||
if (nBlockAlign != 2) {
|
||||
fprintf(stderr, "error: invalid nBlockAlign %u (expected 2)\n", (unsigned)nBlockAlign);
|
||||
error = 1;
|
||||
}
|
||||
if (wBitsPerSample != 16) {
|
||||
fprintf(stderr, "error: invalid wBitsPerSample %u (expected 16)\n", (unsigned)wBitsPerSample);
|
||||
error = 1;
|
||||
}
|
||||
if (cbSize != 0) {
|
||||
fprintf(stderr, "error: invalid cbSize %u (expected 0)\n", (unsigned)cbSize);
|
||||
error = 1;
|
||||
}
|
||||
if (error)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
num_samples = dwOutSize/2;
|
||||
|
||||
/* Write the WAV header. */
|
||||
write_u32(outfp, MAKE_U32('R','I','F','F'));
|
||||
write_u32(outfp, 36 + num_samples*2);
|
||||
write_u32(outfp, MAKE_U32('W','A','V','E'));
|
||||
write_u32(outfp, MAKE_U32('f','m','t',' '));
|
||||
write_u32(outfp, 16);
|
||||
write_u16(outfp, wFormatTag);
|
||||
write_u16(outfp, nChannels);
|
||||
write_u32(outfp, nSamplesPerSec);
|
||||
write_u32(outfp, nAvgBytesPerSec);
|
||||
write_u16(outfp, nBlockAlign);
|
||||
write_u16(outfp, wBitsPerSample);
|
||||
write_u32(outfp, MAKE_U32('d','a','t','a'));
|
||||
write_u32(outfp, num_samples*2);
|
||||
|
||||
/* Decode. */
|
||||
utk_init(&ctx);
|
||||
utk_set_fp(&ctx, infp);
|
||||
|
||||
while (num_samples > 0) {
|
||||
int count = MIN(num_samples, 432);
|
||||
|
||||
utk_decode_frame(&ctx);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
int x = ROUND(ctx.decompressed_frame[i]);
|
||||
write_u16(outfp, (int16_t)CLAMP(x, -32768, 32767));
|
||||
}
|
||||
|
||||
num_samples -= count;
|
||||
}
|
||||
|
||||
if (fclose(outfp) != 0) {
|
||||
fprintf(stderr, "error: failed to close '%s': %s\n", outfile, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fclose(infp);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
1130
library/tools/utkencode/utkencode.c
Normal file
1130
library/tools/utkencode/utkencode.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue