This commit is contained in:
libretroadmin committed 2026-10-11 12:49:10 +02:00
1 parent a0403d4273
commit 4c1ae8b2f1
24 files changed
+5543 -610

No files matched your search

+797 -260
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+8 -3
View File
@@ -356,6 +356,7 @@ struct rchd
* audio/video pays nothing for them. */
uint16_t *av_lookup;
int16_t *av_samples;
uint32_t av_samples_capacity;
struct rchd_av_scratch *av_scratch;
/* One decoded hunk, kept so a range spanning several hunks, or two
@@ -1550,11 +1551,15 @@ static int rchd_decode_avhuff(rchd_t *chd, const uint8_t *src,
if (mode == 0xffff)
{
if (!chd->av_samples)
if (samples > chd->av_samples_capacity)
{
chd->av_samples = (int16_t*)malloc((size_t)samples * sizeof(int16_t));
if (!chd->av_samples)
int16_t *grown = (int16_t*)realloc(chd->av_samples,
(size_t)samples * sizeof(int16_t));
if (!grown)
return RCHD_ERROR_MEM;
chd->av_samples = grown;
chd->av_samples_capacity = samples;
}
fmt.sample_rate = 44100;
+555 -252
View File
File diff suppressed because it is too large. Load diff
+15
View File
@@ -152,3 +152,18 @@ void image_yuv_i420_to_rgb32(uint32_t *dst, unsigned dst_stride,
image_yuv_frame(dst, dst_stride, y, y_stride,
u, u_stride, 1, v, v_stride, 1, w, h, flags);
}
void image_yuv_coefficients(unsigned flags, float c[6])
{
const image_yuv_coef_t *k =
&image_yuv_coefs[((flags & IMAGE_YUV_FLAG_BT709) ? 2 : 0)
| ((flags & IMAGE_YUV_FLAG_FULL_RANGE) ? 1 : 0)];
const float one = (float)(1 << IMAGE_YUV_SHIFT);
c[0] = (float)k->ym / one;
/* the bias less its rounding half, in 0..1 */
c[1] = (float)(k->yb - (1 << (IMAGE_YUV_SHIFT - 1))) / one / 255.0f;
c[2] = (float)k->rv / one;
c[3] = (float)k->gu / one;
c[4] = (float)k->gv / one;
c[5] = (float)k->bu / one;
}
+3
View File
@@ -388,6 +388,7 @@ bool image_loader_finish(image_loader_t *l, struct texture_image *img)
return false;
img->pixels = l->pixels;
img->compressed = l->compressed;
img->planar = NULL;
img->width = l->width;
img->height = l->height;
img->supports_rgba = l->req.rgba;
@@ -445,6 +446,7 @@ static void image_texture_clear(struct texture_image *img)
img->width = 0;
img->height = 0;
img->compressed = NULL;
img->planar = NULL;
img->pix10 = false;
img->fp16 = false;
}
@@ -460,6 +462,7 @@ static bool image_texture_load_buffer_probe(struct texture_image *out_img,
if (!out_img)
return false;
out_img->compressed = NULL;
out_img->planar = NULL;
if (!ptr || !len || !(l = image_loader_new(type, req)))
{
image_texture_clear(out_img);
+7
View File
@@ -1899,6 +1899,13 @@ static uint8_t* hunk_read_compressed(chd_file *chd, uint64_t offset, size_t size
{
size_t bytes;
/* The staging buffer is one uncompressed hunk large. A malformed map
* can advertise a larger 24-bit compressed length; reading that much
* into chd->compressed would overflow it. CHD writers store a hunk
* uncompressed when compression does not make it smaller. */
if (size > chd->header.hunkbytes)
return NULL;
if (chd->file_cache != NULL)
{
return chd->file_cache + offset;
+6
View File
@@ -509,7 +509,13 @@ enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decode
{
struct node_t* node = &decoder->huffnode[curcode];
if (node->numbits > 0)
{
/* An oversubscribed tree assigns codes outside the space for
* this length, which would index past the lookup table. */
if (bithisto[node->numbits] >= (1U << node->numbits))
return HUFFERR_INVALID_DATA;
node->bits = bithisto[node->numbits]++;
}
}
return HUFFERR_NONE;
}
+179 -45
View File
@@ -89,6 +89,12 @@ struct rmpeg1_ps
uint32_t mux_rate;
uint32_t resyncs;
/* Data borrowed from the caller (rmpeg1_ps_borrow), parsed in place:
* NULL when there is none */
const uint8_t *ext;
size_t ext_len;
size_t ext_rd;
bool ended;
bool synced; /* a start code has been located at least once */
};
@@ -156,6 +162,7 @@ void rmpeg1_ps_reset(rmpeg1_ps_t *ps)
ps->mux_rate = 0;
ps->ended = false;
ps->synced = false;
ps->ext = NULL;
/* resyncs is a cumulative health counter and deliberately survives. */
}
@@ -171,7 +178,8 @@ size_t rmpeg1_ps_write(rmpeg1_ps_t *ps, const uint8_t *data, size_t len)
{
size_t room;
if (!ps || !data || len == 0)
/* Not behind data still borrowed, which comes first */
if (!ps || !data || len == 0 || ps->ext)
return 0;
if (ps->capacity - ps->wr < len)
@@ -214,28 +222,29 @@ static uint64_t rmpeg1_ps_read_ts(const uint8_t *p)
*
* Leaves the trailing two bytes unconsumed on failure: a prefix may straddle
* the end of what we have been given so far. */
static size_t rmpeg1_ps_find_start(rmpeg1_ps_t *ps)
static size_t rmpeg1_ps_find_start(const uint8_t *buf, size_t *rdp,
size_t wr)
{
size_t i;
size_t end;
if (ps->wr < ps->rd + 3)
if (wr < *rdp + 3)
return (size_t)-1;
end = ps->wr - 2;
end = wr - 2;
for (i = ps->rd; i < end; i++)
for (i = *rdp; i < end; i++)
{
if ( ps->buf[i ] == 0x00
&& ps->buf[i + 1] == 0x00
&& ps->buf[i + 2] == 0x01)
if ( buf[i ] == 0x00
&& buf[i + 1] == 0x00
&& buf[i + 2] == 0x01)
return i;
}
/* Nothing found. Everything before the last two bytes is garbage and can
* be dropped so the buffer does not grow without bound on a broken
* stream. */
ps->rd = end;
*rdp = end;
return (size_t)-1;
}
@@ -273,10 +282,11 @@ static uint8_t rmpeg1_ps_classify(uint8_t stream_id, uint8_t *index)
/* Parser */
/* --------------------------------------------------------------------- */
int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
/* One packet out of buf[*rdp..wr): 1, or 0 with *rdp at the start of
* what is not yet a whole unit */
static int rmpeg1_ps_parse(rmpeg1_ps_t *ps, const uint8_t *buf,
size_t *rdp, size_t wr, rmpeg1_ps_packet_t *out)
{
if (!ps || !out)
return 0;
for (;;)
{
@@ -295,7 +305,7 @@ int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
if (ps->ended)
return 0;
sc = rmpeg1_ps_find_start(ps);
sc = rmpeg1_ps_find_start(buf, rdp, wr);
if (sc == (size_t)-1)
return 0;
@@ -305,14 +315,14 @@ int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
* legal and expected. Only count a resync when the skipped bytes are
* something other than zero fill, so the counter stays meaningful as a
* stream-health signal rather than firing once per sector. */
if (sc != ps->rd)
if (sc != *rdp)
{
size_t g;
bool fill = true;
for (g = ps->rd; g < sc; g++)
for (g = *rdp; g < sc; g++)
{
if (ps->buf[g] != 0x00)
if (buf[g] != 0x00)
{
fill = false;
break;
@@ -322,21 +332,21 @@ int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
if (!fill || !ps->synced)
ps->resyncs++;
ps->rd = sc;
*rdp = sc;
}
ps->synced = true;
avail = ps->wr - ps->rd;
avail = wr - *rdp;
if (avail < 4)
return 0;
stream_id = ps->buf[ps->rd + 3];
stream_id = buf[*rdp + 3];
/* --- end code ------------------------------------------------- */
if (stream_id == RMPEG1_PS_END_CODE)
{
ps->rd += 4;
*rdp += 4;
ps->ended = true;
return 0;
}
@@ -349,14 +359,38 @@ int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
if (avail < 12)
return 0;
p = ps->buf + ps->rd + 4;
p = buf + *rdp + 4;
/* MPEG-1 packs carry '0010' here. MPEG-2 uses '01', and its pack
* header is 14 bytes with a different SCR layout; we do not claim
* to parse those, so resync past it rather than misread it. */
/* MPEG-2 (ISO/IEC 13818-1) packs carry '01' here: 14 bytes, the
* SCR in another layout, a 22-bit mux rate and up to 7 bytes of
* stuffing after. */
if ((p[0] & 0xC0) == 0x40)
{
size_t len;
if (avail < 14)
return 0;
len = 14 + (size_t)(p[9] & 0x07);
if (avail < len)
return 0;
ps->scr = ((uint64_t)((p[0] >> 3) & 0x07) << 30)
| ((uint64_t)( p[0] & 0x03) << 28)
| ((uint64_t) p[1] << 20)
| ((uint64_t)((p[2] >> 3) & 0x1F) << 15)
| ((uint64_t)( p[2] & 0x03) << 13)
| ((uint64_t) p[3] << 5)
| (uint64_t)((p[4] >> 3) & 0x1F);
ps->mux_rate = ((uint32_t)p[6] << 14)
| ((uint32_t)p[7] << 6)
| ((uint32_t)p[8] >> 2);
*rdp += len;
continue;
}
/* MPEG-1 packs carry '0010' here: anything else is not a pack
* header we know, so resync past it rather than misread it. */
if ((p[0] & 0xF0) != 0x20)
{
ps->rd += 4;
*rdp += 4;
ps->resyncs++;
continue;
}
@@ -367,7 +401,7 @@ int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
| ((uint32_t)p[6] << 7)
| ((uint32_t)p[7] >> 1);
ps->rd += 12;
*rdp += 12;
continue;
}
@@ -375,7 +409,7 @@ int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
if (avail < 6)
return 0;
packet_len = ((size_t)ps->buf[ps->rd + 4] << 8) | ps->buf[ps->rd + 5];
packet_len = ((size_t)buf[*rdp + 4] << 8) | buf[*rdp + 5];
if (avail < 6 + packet_len)
{
@@ -383,21 +417,22 @@ int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
* this start code and resync rather than stalling forever. */
if (6 + packet_len > ps->capacity)
{
ps->rd += 4;
*rdp += 4;
ps->resyncs++;
continue;
}
return 0;
}
pos = ps->rd + 6;
pos = *rdp + 6;
payload_end = pos + packet_len;
/* System headers describe the stream set; they carry no elementary
* data, so record nothing and move on. */
if (stream_id == RMPEG1_PS_SYSTEM_HEADER)
{
ps->rd = payload_end;
*rdp = payload_end;
continue;
}
@@ -405,7 +440,7 @@ int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
if (type == RMPEG1_PS_PADDING)
{
ps->rd = payload_end;
*rdp = payload_end;
continue;
}
@@ -413,76 +448,99 @@ int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
* recognise) are skipped whole. */
if (type == RMPEG1_PS_NONE)
{
ps->rd = payload_end;
*rdp = payload_end;
continue;
}
/* private_stream_2 has no header extension at all: payload starts
* immediately. Everything else gets the stuffing/STD/timestamp
* preamble. */
if (type != RMPEG1_PS_PRIVATE_2)
/* MPEG-2: '10', two flag bytes and the length of the optional
* fields, the timestamps first among them */
if ( type != RMPEG1_PS_PRIVATE_2 && pos + 3 <= payload_end
&& (buf[pos] & 0xC0) == 0x80)
{
unsigned flags = buf[pos + 1] >> 6;
size_t hdr = pos + 3;
size_t hlen = buf[pos + 2];
if ( hdr + hlen > payload_end
|| flags == 1
|| (flags == 2 && hlen < 5)
|| (flags == 3 && hlen < 10))
{
*rdp = payload_end;
ps->resyncs++;
continue;
}
if (flags & 2)
pts = rmpeg1_ps_read_ts(buf + hdr);
if (flags == 3)
dts = rmpeg1_ps_read_ts(buf + hdr + 5);
pos = hdr + hlen;
}
else if (type != RMPEG1_PS_PRIVATE_2)
{
/* Up to 16 stuffing bytes. The spec caps it; enforcing the cap
* stops a run of FFh in corrupt data from eating the packet. */
stuffing = 0;
while (pos < payload_end && ps->buf[pos] == 0xFF && stuffing < 16)
while (pos < payload_end && buf[pos] == 0xFF && stuffing < 16)
{
pos++;
stuffing++;
}
if (pos + 1 < payload_end && (ps->buf[pos] & 0xC0) == 0x40)
if (pos + 1 < payload_end && (buf[pos] & 0xC0) == 0x40)
pos += 2; /* STD buffer scale and size */
if (pos < payload_end)
{
uint8_t flag = (uint8_t)(ps->buf[pos] & 0xF0);
uint8_t flag = (uint8_t)(buf[pos] & 0xF0);
if (flag == 0x20)
{
if (pos + 5 > payload_end)
{
ps->rd = payload_end;
*rdp = payload_end;
ps->resyncs++;
continue;
}
pts = rmpeg1_ps_read_ts(ps->buf + pos);
pts = rmpeg1_ps_read_ts(buf + pos);
pos += 5;
}
else if (flag == 0x30)
{
if (pos + 10 > payload_end)
{
ps->rd = payload_end;
*rdp = payload_end;
ps->resyncs++;
continue;
}
pts = rmpeg1_ps_read_ts(ps->buf + pos);
dts = rmpeg1_ps_read_ts(ps->buf + pos + 5);
pts = rmpeg1_ps_read_ts(buf + pos);
dts = rmpeg1_ps_read_ts(buf + pos + 5);
pos += 10;
}
else if (ps->buf[pos] == 0x0F)
else if (buf[pos] == 0x0F)
pos += 1;
else
{
/* None of the three legal encodings. The header is malformed;
* drop the packet rather than emit whatever follows as if it
* were elementary data. */
ps->rd = payload_end;
*rdp = payload_end;
ps->resyncs++;
continue;
}
}
}
ps->rd = payload_end;
*rdp = payload_end;
/* A packet whose header consumed everything is legal and carries no
* data. Nothing downstream wants a zero-length buffer, so skip it. */
if (pos >= payload_end)
continue;
out->data = ps->buf + pos;
out->data = buf + pos;
out->size = payload_end - pos;
out->pts = pts;
out->dts = dts;
@@ -494,6 +552,82 @@ int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
}
}
/* Bytes the unit at the start of the buffered data needs before it can
* be parsed whole; at least one */
static size_t rmpeg1_ps_unit_need(const rmpeg1_ps_t *ps)
{
const uint8_t *b = ps->buf + ps->rd;
size_t avail = ps->wr - ps->rd;
size_t need;
if (avail < 6 || b[0] || b[1] || b[2] != 1)
need = 6;
else if (b[3] == RMPEG1_PS_PACK_START)
need = (b[4] & 0xC0) == 0x40
? (avail >= 14 ? 14 + (size_t)(b[13] & 0x07) : 14) : 12;
else if (b[3] == RMPEG1_PS_END_CODE)
need = 4;
else
need = 6 + (((size_t)b[4] << 8) | b[5]);
return need > avail ? need - avail : 1;
}
/* @n borrowed bytes into the buffer, made room for */
static void rmpeg1_ps_take(rmpeg1_ps_t *ps, size_t n)
{
if (ps->capacity - ps->wr < n)
rmpeg1_ps_compact(ps);
memcpy(ps->buf + ps->wr, ps->ext + ps->ext_rd, n);
ps->wr += n;
ps->ext_rd += n;
if (ps->ext_rd == ps->ext_len)
ps->ext = NULL;
}
int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out)
{
if (!ps || !out)
return 0;
for (;;)
{
/* What was written, or a unit borrowed data ended part way into */
if (ps->rd < ps->wr)
{
size_t n;
if (rmpeg1_ps_parse(ps, ps->buf, &ps->rd, ps->wr, out))
return 1;
if (!ps->ext || ps->ended)
return 0;
/* Completed from the borrowed data, as much of it as it takes */
n = rmpeg1_ps_unit_need(ps);
if (n > ps->ext_len - ps->ext_rd)
n = ps->ext_len - ps->ext_rd;
rmpeg1_ps_take(ps, n);
continue;
}
if (!ps->ext)
return 0;
ps->rd = ps->wr = 0;
/* Borrowed data is parsed where it lies */
if (rmpeg1_ps_parse(ps, ps->ext, &ps->ext_rd, ps->ext_len, out))
return 1;
/* A unit it ends part way into is kept for the next */
if (ps->ext && ps->ext_rd < ps->ext_len && !ps->ended)
rmpeg1_ps_take(ps, ps->ext_len - ps->ext_rd);
ps->ext = NULL;
return 0;
}
}
size_t rmpeg1_ps_borrow(rmpeg1_ps_t *ps, const uint8_t *data, size_t len)
{
if (!ps || !data || !len || ps->ext)
return 0;
ps->ext = data;
ps->ext_len = len;
ps->ext_rd = 0;
return len;
}
/* --------------------------------------------------------------------- */
/* Accessors */
/* --------------------------------------------------------------------- */
+14 -8
View File
@@ -3167,6 +3167,20 @@ bool rpng_iterate_image(rpng_t *rpng)
if (!rpng_process_ihdr(&rpng->ihdr))
return false;
/* PNG only defines compression and filter method 0, and
* interlace methods 0 (none) and 1 (Adam7). In particular,
* the decode path treats every non-zero interlace value as
* Adam7 while the buffer setup only treats 1 as Adam7. */
if ( rpng->ihdr.compression != 0
|| rpng->ihdr.filter != 0
|| rpng->ihdr.interlace > 1)
{
#if defined(DEBUG) || defined(RPNG_TEST)
fprintf(stderr, "[RPNG] Error in line %d.\n", __LINE__);
#endif
return false;
}
if (rpng->ihdr.width == 0 || rpng->ihdr.height == 0)
return false;
@@ -3212,14 +3226,6 @@ bool rpng_iterate_image(rpng_t *rpng)
return false;
}
if (rpng->ihdr.compression != 0)
{
#if defined(DEBUG) || defined(RPNG_TEST)
fprintf(stderr, "[RPNG] Error in line %d.\n", __LINE__);
#endif
return false;
}
rpng->flags |= RPNG_FLAG_HAS_IHDR;
break;
+312
View File
@@ -0,0 +1,312 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rpsmf.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* 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 OR COPYRIGHT HOLDERS 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.
*/
#include <stdlib.h>
#include <string.h>
#include <formats/rpsmf.h>
#include <formats/rmpeg1_ps.h>
#define RPSMF_SUBSTREAMS 16
/* A frame header and the largest frame after it */
#define RPSMF_FRAME_MAX (8 + RPSMF_ATRAC3P_FRAME_BYTES(3, 255))
/* Where in a substream's first packet the first frame may start */
#define RPSMF_SKIP_MAX 16
typedef struct
{
uint8_t frame[RPSMF_FRAME_MAX];
size_t have; /* bytes of the frame held, its header among them */
size_t need; /* the whole frame, once its header is in; else 0 */
uint64_t pts;
int skip; /* the packets' own header bytes; -1 until learnt */
} rpsmf_sub_t;
struct rpsmf
{
rmpeg1_ps_t *ps;
/* The audio packet being cut into frames, where the demuxer has it:
* nothing is written or borrowed until it is used up */
const uint8_t *pkt;
size_t pkt_len;
size_t pkt_pos;
uint64_t pkt_pts; /* for the first frame to start in it */
int pkt_sub;
uint32_t resyncs;
rpsmf_sub_t sub[RPSMF_SUBSTREAMS];
};
static uint32_t rpsmf_be32(const uint8_t *p)
{
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16)
| ((uint32_t)p[2] << 8) | (uint32_t)p[3];
}
bool rpsmf_parse_header(const uint8_t *buf, size_t len, rpsmf_info_t *out)
{
unsigned i;
if ( !buf || !out || len < RPSMF_HEADER_SIZE
|| memcmp(buf, "PSMF", 4))
return false;
out->version = 0;
for (i = 4; i < 8; i++)
{
if (buf[i] < '0' || buf[i] > '9')
return false;
out->version = out->version * 10 + (uint32_t)(buf[i] - '0');
}
out->data_offset = rpsmf_be32(buf + 0x08);
out->data_size = rpsmf_be32(buf + 0x0C);
out->tick_freq = rpsmf_be32(buf + 0x56);
out->duration = rpsmf_be32(buf + 0x5C);
out->mux_rate = rpsmf_be32(buf + 0x60);
return out->data_offset >= RPSMF_HEADER_SIZE;
}
static bool rpsmf_frame_header(const uint8_t *p)
{
return p[0] == 0x0F && p[1] == 0xD0
&& !p[4] && !p[5] && !p[6] && !p[7];
}
static void rpsmf_sub_clear(rpsmf_sub_t *s)
{
s->have = 0;
s->need = 0;
s->pts = RPSMF_NO_PTS;
s->skip = -1;
}
rpsmf_t *rpsmf_init(void)
{
rpsmf_t *d = (rpsmf_t*)calloc(1, sizeof(*d));
if (!d)
return NULL;
if (!(d->ps = rmpeg1_ps_init(0)))
{
free(d);
return NULL;
}
rpsmf_reset(d);
return d;
}
void rpsmf_free(rpsmf_t *d)
{
if (!d)
return;
rmpeg1_ps_free(d->ps);
free(d);
}
void rpsmf_reset(rpsmf_t *d)
{
unsigned i;
if (!d)
return;
rmpeg1_ps_reset(d->ps);
d->pkt_len = 0;
d->pkt_pos = 0;
for (i = 0; i < RPSMF_SUBSTREAMS; i++)
rpsmf_sub_clear(&d->sub[i]);
}
size_t rpsmf_write(rpsmf_t *d, const uint8_t *data, size_t len)
{
return (d && d->pkt_pos >= d->pkt_len)
? rmpeg1_ps_write(d->ps, data, len) : 0;
}
size_t rpsmf_borrow(rpsmf_t *d, const uint8_t *data, size_t len)
{
return (d && d->pkt_pos >= d->pkt_len)
? rmpeg1_ps_borrow(d->ps, data, len) : 0;
}
uint32_t rpsmf_resyncs(const rpsmf_t *d)
{
return d ? d->resyncs : 0;
}
/* The next frame header in the packet from its cursor on: the cursor
* there and true, or past the end and false */
static bool rpsmf_seek_header(rpsmf_t *d)
{
while (d->pkt_pos + 8 <= d->pkt_len)
{
if (rpsmf_frame_header(d->pkt + d->pkt_pos))
return true;
d->pkt_pos++;
}
d->pkt_pos = d->pkt_len;
return false;
}
/* More of the current audio packet into its substream's frame: 1 and
* *out when a frame is whole, 0 when the packet is used up */
static int rpsmf_cut(rpsmf_t *d, rpsmf_packet_t *out)
{
rpsmf_sub_t *s = &d->sub[d->pkt_sub];
while (d->pkt_pos < d->pkt_len)
{
size_t take;
/* A frame whole within the packet goes out from where it lies */
if (!s->have && d->pkt_len - d->pkt_pos >= 8
&& rpsmf_frame_header(d->pkt + d->pkt_pos))
{
const uint8_t *f = d->pkt + d->pkt_pos;
size_t n = RPSMF_ATRAC3P_FRAME_BYTES(f[2], f[3]);
if (8 + n <= d->pkt_len - d->pkt_pos)
{
out->data = f + 8;
out->size = n;
out->pts = d->pkt_pts;
out->dts = RPSMF_NO_PTS;
out->kind = RPSMF_ATRAC3P;
out->stream = (uint8_t)d->pkt_sub;
out->params[0] = f[2];
out->params[1] = f[3];
d->pkt_pts = RPSMF_NO_PTS;
d->pkt_pos += 8 + n;
return 1;
}
}
if (!s->need)
{
/* A frame starts here: the packet's timestamp is the first
* such frame's */
if (!s->have)
{
s->pts = d->pkt_pts;
d->pkt_pts = RPSMF_NO_PTS;
}
take = 8 - s->have;
if (take > d->pkt_len - d->pkt_pos)
take = d->pkt_len - d->pkt_pos;
memcpy(s->frame + s->have, d->pkt + d->pkt_pos, take);
s->have += take;
d->pkt_pos += take;
if (s->have < 8)
return 0;
if (!rpsmf_frame_header(s->frame))
{
/* Not where a frame should be: the next header there is */
d->resyncs++;
s->have = 0;
if (!rpsmf_seek_header(d))
return 0;
continue;
}
s->need = 8 + RPSMF_ATRAC3P_FRAME_BYTES(s->frame[2], s->frame[3]);
}
take = s->need - s->have;
if (take > d->pkt_len - d->pkt_pos)
take = d->pkt_len - d->pkt_pos;
memcpy(s->frame + s->have, d->pkt + d->pkt_pos, take);
s->have += take;
d->pkt_pos += take;
if (s->have == s->need)
{
out->data = s->frame + 8;
out->size = s->need - 8;
out->pts = s->pts;
out->dts = RPSMF_NO_PTS;
out->kind = RPSMF_ATRAC3P;
out->stream = (uint8_t)d->pkt_sub;
out->params[0] = s->frame[2];
out->params[1] = s->frame[3];
s->have = 0;
s->need = 0;
return 1;
}
}
return 0;
}
int rpsmf_next(rpsmf_t *d, rpsmf_packet_t *out)
{
rmpeg1_ps_packet_t pkt;
if (!d || !out)
return 0;
for (;;)
{
if (d->pkt_pos < d->pkt_len && rpsmf_cut(d, out))
return 1;
if (!rmpeg1_ps_next(d->ps, &pkt))
return 0;
if (pkt.type == RMPEG1_PS_VIDEO)
{
out->data = pkt.data;
out->size = pkt.size;
out->pts = pkt.pts;
out->dts = pkt.dts;
out->kind = RPSMF_VIDEO;
out->stream = pkt.index;
out->params[0] = out->params[1] = 0;
return 1;
}
if (pkt.type != RMPEG1_PS_PRIVATE_1 || pkt.size < 2)
continue;
if (pkt.data[0] >= RPSMF_SUBSTREAMS)
{
out->data = pkt.data + 1;
out->size = pkt.size - 1;
out->pts = pkt.pts;
out->dts = RPSMF_NO_PTS;
out->kind = RPSMF_PRIVATE;
out->stream = pkt.data[0];
out->params[0] = out->params[1] = 0;
return 1;
}
{
rpsmf_sub_t *s = &d->sub[pkt.data[0]];
d->pkt = pkt.data + 1;
d->pkt_len = pkt.size - 1;
d->pkt_pos = 0;
d->pkt_pts = pkt.pts;
d->pkt_sub = pkt.data[0];
/* The substream's first packet starts a frame right after the
* packet's own header: how long that is, for every packet */
if (s->skip < 0)
{
int i;
for (i = 0; i <= RPSMF_SKIP_MAX
&& (size_t)i + 8 <= d->pkt_len; i++)
if (rpsmf_frame_header(d->pkt + i))
break;
if (i > RPSMF_SKIP_MAX || (size_t)i + 8 > d->pkt_len)
{
d->resyncs++;
d->pkt_len = 0;
continue;
}
s->skip = i;
}
if ((size_t)s->skip >= d->pkt_len)
{
d->pkt_len = 0;
continue;
}
d->pkt_pos = (size_t)s->skip;
}
}
}
+71 -1
View File
@@ -48,6 +48,19 @@ enum image_process_code
struct texture_compressed; /* defined below, after enum image_type_enum */
/* An 8-bit YCbCr 4:2:0 frame where it lies. Plane 0 is luma; planes
* 1 and 2 are Cb and Cr, @chroma_step bytes between neighbours (1 for
* I420; 2 for NV12, whose one interleaved plane is planes 1 and 2 at
* offsets 0 and 1). Strides in bytes; @yuv is IMAGE_YUV_FLAG_*
* (formats/image_yuv_blit.h). */
struct texture_planar
{
const uint8_t *planes[3];
unsigned strides[3];
unsigned chroma_step;
unsigned yuv;
};
struct texture_image
{
uint32_t *pixels;
@@ -69,6 +82,9 @@ struct texture_image
* image_texture_realize_rgba() decodes to ->pixels on demand for
* drivers that cannot sample the format. */
struct texture_compressed *compressed;
/* When non-NULL the frame is this, ->pixels is NULL, and only a
* driver answering TEXTURE_GPU_FORMAT_YUV420 is given it. */
const struct texture_planar *planar;
};
enum image_type_enum
@@ -124,7 +140,10 @@ enum texture_gpu_format
* texture drawn in the menu or over content is shown as linear
* scRGB - 1.0 at 80 nits, the 709 primaries - rather than as an
* SDR-encoded one: true only while the output is HDR. */
TEXTURE_GPU_FORMAT_SCRGB
TEXTURE_GPU_FORMAT_SCRGB,
/* 8-bit YCbCr 4:2:0 on ->planar (I420 or NV12), converted to RGB
* on the GPU: whether load_texture and update_texture take one. */
TEXTURE_GPU_FORMAT_YUV420
};
/* Numeric mip layout reported by a loader without decoding. Offsets are
@@ -170,6 +189,57 @@ enum image_type_enum image_texture_get_type(const char *path);
* hook, or at the byte frontier. image_texture_load() is this loader
* run to completion in one call. */
/* Pixel layouts, a bit each, so a consumer can accept several and a
* producer pick the best it can emit. A name is a promise about the
* layout, not about any consumer taking it. The frontend's surfaces
* and a core's own renderer name formats the same way. */
enum image_pixfmt
{
/* 8 bits a channel in a 32-bit word, ARGB or memory-order RGBA */
IMAGE_PIXFMT_8888 = (1 << 0),
/* XRGB2101010: bits [29:20]=R [19:10]=G [9:0]=B */
IMAGE_PIXFMT_2101010 = (1 << 1),
/* Half float a channel, linear scRGB (1.0 = 80 nits) */
IMAGE_PIXFMT_FP16 = (1 << 2),
IMAGE_PIXFMT_FP32 = (1 << 3),
IMAGE_PIXFMT_565 = (1 << 4),
IMAGE_PIXFMT_4444 = (1 << 5),
/* GX RGBA8, the GameCube/Wii layout: 4x4 tiles of 64 bytes, the AR
* halves of a tile's 16 texels then their GB halves, both axes
* multiples of 4 (image_texture_tile_gx) */
IMAGE_PIXFMT_GX_RGBA8 = (1 << 6),
/* YCbCr 4:2:0, as video decoders and cameras produce it: a luma
* plane, then chroma at half resolution on both axes. I420 has the
* two chroma planes apart, NV12 interleaves them Cb,Cr in one, P010
* is NV12 with 16-bit samples holding 10 bits in the high bits. */
IMAGE_PIXFMT_I420 = (1 << 7),
IMAGE_PIXFMT_NV12 = (1 << 8),
IMAGE_PIXFMT_P010 = (1 << 9)
};
#define IMAGE_PIXFMT_PLANAR \
(IMAGE_PIXFMT_I420 | IMAGE_PIXFMT_NV12 | IMAGE_PIXFMT_P010)
/* Planes of one image_pixfmt bit */
#define IMAGE_PIXFMT_PLANES(f) \
(((f) & IMAGE_PIXFMT_I420) ? 3u \
: ((f) & (IMAGE_PIXFMT_NV12 | IMAGE_PIXFMT_P010)) ? 2u : 1u)
/* Bytes a texel of a packed format takes, or a luma sample of a planar
* one */
#define IMAGE_PIXFMT_BPP(f) \
(((f) & IMAGE_PIXFMT_FP32) ? 16u \
: ((f) & IMAGE_PIXFMT_FP16) ? 8u \
: ((f) & (IMAGE_PIXFMT_565 | IMAGE_PIXFMT_4444 | IMAGE_PIXFMT_P010)) ? 2u \
: ((f) & (IMAGE_PIXFMT_I420 | IMAGE_PIXFMT_NV12)) ? 1u : 4u)
/* Bytes of a whole tightly packed @w x @h frame, every plane of it */
#define IMAGE_PIXFMT_FRAME_SIZE(f, w, h) \
((size_t)(w) * (size_t)(h) * IMAGE_PIXFMT_BPP(f) \
+ (((f) & IMAGE_PIXFMT_PLANAR) \
? 2u * (((size_t)(w) + 1) >> 1) * (((size_t)(h) + 1) >> 1) \
* IMAGE_PIXFMT_BPP(f) : 0u))
/* What the caller takes of a still, asked of it once by whoever knows
* (the video driver, a core's own renderer) and handed down. Zero is
* the ordinary 8-bit image, ARGB words. */
+8
View File
@@ -66,6 +66,14 @@ void image_yuv_i420_to_rgb32(uint32_t *dst, unsigned dst_stride,
const uint8_t *v, unsigned v_stride,
unsigned w, unsigned h, unsigned flags);
/* The matrix @flags names (BT709, FULL_RANGE), for a GPU to apply to
* samples normalised to 0..1: with chroma less 128/255,
* R = c[0] * Y + c[1] + c[2] * Cr
* G = c[0] * Y + c[1] + c[3] * Cb + c[4] * Cr
* B = c[0] * Y + c[1] + c[5] * Cb
* the same matrix the conversions above use. */
void image_yuv_coefficients(unsigned flags, float c[6]);
RETRO_END_DECLS
#endif
+95
View File
@@ -0,0 +1,95 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (ratrac3.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* 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 OR COPYRIGHT HOLDERS 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.
*/
#ifndef __LIBRETRO_SDK_FORMAT_RATRAC3_H
#define __LIBRETRO_SDK_FORMAT_RATRAC3_H
/* ATRAC3 audio decoder (Sony's LP2/LP4 codec, as in PSP .at3 files,
* WAVE format tag 270h, and OpenMG).
*
* Written clean-room: the bit-stream syntax from public format notes,
* the signal path measured against an existing decoder's output, never
* its source. Every frame decodes to 1024 samples a channel.
*
* A frame is block_align bytes: one sound unit a channel, block_align /
* channels bytes each, or, in joint stereo (the 66 kb/s LP4 mode), a
* mid unit read forwards from the start and a side unit read backwards
* from the end, with the stereo parameters at the very end.
*
* ratrac3_info_t info;
* ratrac3_parse_wave_fmt(fmt_chunk, fmt_len, &info);
* ratrac3_t *d = ratrac3_new(info.channels, info.block_align,
* info.joint_stereo);
* while (have a frame)
* ratrac3_decode_s16(d, frame, info.block_align, pcm);
*
* C89. A decoder holds no pointer into the input between calls. */
#include <stddef.h>
#include <stdint.h>
#include <retro_common_api.h>
#include <boolean.h>
RETRO_BEGIN_DECLS
#define RATRAC3_SAMPLES 1024 /* a channel, every frame */
#define RATRAC3_FORMAT_TAG 0x0270
typedef struct ratrac3 ratrac3_t;
typedef struct
{
unsigned channels; /* 1 or 2 */
unsigned sample_rate; /* 44100 as a rule */
unsigned block_align; /* bytes a frame */
unsigned bitrate; /* bits a second */
bool joint_stereo;
} ratrac3_info_t;
/* Reads a WAVE 'fmt ' chunk's body (format tag 270h and its 14 bytes
* of extra data). Returns false when it is not ATRAC3 or not a layout
* this decoder takes. */
bool ratrac3_parse_wave_fmt(const uint8_t *fmt, size_t len,
ratrac3_info_t *info);
/* channels 1 or 2; block_align as the container gives it; joint stereo
* only with two channels. NULL on a layout the decoder does not take. */
ratrac3_t *ratrac3_new(unsigned channels, unsigned block_align,
bool joint_stereo);
void ratrac3_free(ratrac3_t *d);
/* Forgets the signal history, as at a seek. */
void ratrac3_reset(ratrac3_t *d);
/* Decodes one frame of len (at least block_align) bytes into
* RATRAC3_SAMPLES samples a channel, interleaved. Returns
* RATRAC3_SAMPLES, or -1 on a frame that does not parse: the output
* is then silence and the history is kept as it was. */
int ratrac3_decode_s16(ratrac3_t *d, const uint8_t *frame, size_t len,
int16_t *out);
int ratrac3_decode_float(ratrac3_t *d, const uint8_t *frame, size_t len,
float *out);
RETRO_END_DECLS
#endif
+11 -1
View File
@@ -3,7 +3,9 @@
/* MPEG-1 Systems (Program Stream) demultiplexer.
*
* Written from ISO/IEC 11172-1. Splits an MPEG-1 Program Stream into its
* Written from ISO/IEC 11172-1, and takes the MPEG-2 program stream of
* ISO/IEC 13818-1 as well - its pack and PES headers, packet by packet -
* which is what a PSP movie (rpsmf.h) and a DVD's VOBs are. Splits an MPEG-1 Program Stream into its
* constituent elementary streams (MPEG-1 video, MPEG-1 audio layer I/II/III,
* private and padding streams) and recovers the timestamps attached to them.
*
@@ -82,6 +84,14 @@ size_t rmpeg1_ps_space(const rmpeg1_ps_t *ps);
* when the buffer is full; drain with rmpeg1_ps_next() and retry the rest. */
size_t rmpeg1_ps_write(rmpeg1_ps_t *ps, const uint8_t *data, size_t len);
/* Hand over input to be parsed where it lies, rather than copied in: the
* packets that lie whole within it point into it, and a unit it ends part
* way into is copied, to be completed by what comes next. @data must stay
* valid and unchanged until rmpeg1_ps_next() returns 0. Returns @len, or 0
* while data borrowed before is still being parsed; rmpeg1_ps_write()
* likewise takes nothing until it has been. */
size_t rmpeg1_ps_borrow(rmpeg1_ps_t *ps, const uint8_t *data, size_t len);
/* Pull one packet. Returns 1 and fills *out on success, 0 when more input is
* needed. Padding packets are consumed silently and never returned. */
int rmpeg1_ps_next(rmpeg1_ps_t *ps, rmpeg1_ps_packet_t *out);
+128
View File
@@ -0,0 +1,128 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rpsmf.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* 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 OR COPYRIGHT HOLDERS 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.
*/
#ifndef __LIBRETRO_SDK_FORMAT_RPSMF_H
#define __LIBRETRO_SDK_FORMAT_RPSMF_H
/* PSP movie (PSMF, .pmf) demultiplexer.
*
* A PSMF file is a 2048-byte header and an MPEG-2 program stream: H.264
* video in stream E0h, ATRAC3plus audio in private stream 1, substream
* by substream. The stream goes through rmpeg1_ps; this layer reads the
* header, passes video on as it comes, and cuts the audio into whole
* ATRAC3plus frames.
*
* An ATRAC3plus frame in a PSMF is an 8-byte header - 0Fh D0h, two bytes
* of parameters, four of zero - and (((p0 & 3) << 8) | p1) * 8 + 8 bytes
* of frame. Each private stream 1 packet starts with the substream byte
* and a few header bytes of its own before the frame data, which runs on
* across packets; the number of those bytes is learnt from the first
* packet of a substream, where a frame starts right after them, and
* checked against every frame header after. A frame that does not chain
* resynchronises on the next frame header.
*
* Usage is push/pull, as rmpeg1_ps's:
*
* rpsmf_info_t info;
* rpsmf_parse_header(file, file_len, &info);
* rpsmf_t *d = rpsmf_init();
* rpsmf_write(d, file + info.data_offset, n);
* while (rpsmf_next(d, &pkt))
* ...
*
* Packet data points into the demuxer, or into borrowed input, and stays
* valid until the next rpsmf_next(). C89. */
#include <stddef.h>
#include <stdint.h>
#include <retro_common_api.h>
#include <boolean.h>
RETRO_BEGIN_DECLS
#define RPSMF_HEADER_SIZE 0x64
#define RPSMF_NO_PTS ((uint64_t)UINT64_C(0xFFFFFFFFFFFFFFFF))
typedef struct
{
uint32_t version; /* "0012" .. "0015" as 12 .. 15 */
uint32_t data_offset; /* where the program stream starts, 2048 */
uint32_t data_size; /* its length from there */
uint32_t tick_freq; /* ticks a second of @duration */
uint32_t duration;
uint32_t mux_rate; /* 50 bytes/s, as the pack headers say */
} rpsmf_info_t;
enum rpsmf_kind
{
RPSMF_NONE = 0,
RPSMF_VIDEO, /* H.264 byte stream, as the PES carried it */
RPSMF_ATRAC3P, /* one whole ATRAC3plus frame */
RPSMF_PRIVATE /* another private stream 1 substream, raw */
};
typedef struct
{
const uint8_t *data;
size_t size;
uint64_t pts; /* 90 kHz, or RPSMF_NO_PTS */
uint64_t dts; /* video only; 90 kHz, or RPSMF_NO_PTS */
uint8_t kind; /* enum rpsmf_kind */
uint8_t stream; /* E0h's low nibble, or the substream */
/* An ATRAC3plus frame's two parameter bytes, as its header has them:
* the frame size, and the channel layout and rate of the stream */
uint8_t params[2];
} rpsmf_packet_t;
/* The header's fields from the first RPSMF_HEADER_SIZE bytes of a file.
* False when it is not a PSMF or @len is short. */
bool rpsmf_parse_header(const uint8_t *buf, size_t len, rpsmf_info_t *out);
/* Bytes of an ATRAC3plus frame after its header, from the two parameter
* bytes */
#define RPSMF_ATRAC3P_FRAME_BYTES(p0, p1) \
((((size_t)((p0) & 3) << 8) | (size_t)(p1)) * 8 + 8)
typedef struct rpsmf rpsmf_t;
rpsmf_t *rpsmf_init(void);
void rpsmf_free(rpsmf_t *d);
/* After a seek: everything buffered and every partial frame goes */
void rpsmf_reset(rpsmf_t *d);
/* Program stream bytes, from the header's data_offset on, copied in.
* Returns what was taken: less than @len only when full, or nothing while
* the last packet is not yet drained by rpsmf_next(). */
size_t rpsmf_write(rpsmf_t *d, const uint8_t *data, size_t len);
/* The same parsed where it lies (rmpeg1_ps_borrow): video packets and
* the audio frames whole within one packet point into @data, which stays
* valid and unchanged until rpsmf_next() returns 0. No copy but the
* frames that run across packets, and what @data ends part way into. */
size_t rpsmf_borrow(rpsmf_t *d, const uint8_t *data, size_t len);
/* One packet: 1 and *out filled, 0 when more input is needed */
int rpsmf_next(rpsmf_t *d, rpsmf_packet_t *out);
/* Frames dropped because they did not chain */
uint32_t rpsmf_resyncs(const rpsmf_t *d);
RETRO_END_DECLS
#endif
+31 -6
View File
@@ -130,8 +130,8 @@ RETRO_BEGIN_DECLS
* subtraction would produce a giant array; guard with a max() so
* the pad is always at least 1 byte and never underflows. */
#define RETRO_SPSC_PAD0_BYTES \
((RETRO_SPSC_CACHE_LINE > (sizeof(uint8_t*) + sizeof(size_t))) \
? (RETRO_SPSC_CACHE_LINE - (sizeof(uint8_t*) + sizeof(size_t))) \
((RETRO_SPSC_CACHE_LINE > (sizeof(uint8_t*) + 3 * sizeof(size_t))) \
? (RETRO_SPSC_CACHE_LINE - (sizeof(uint8_t*) + 3 * sizeof(size_t))) \
: 1)
/* Each cursor shares its line with that side's private copy of the
* other cursor (see cached_tail / cached_head below), so the pad is
@@ -144,7 +144,14 @@ RETRO_BEGIN_DECLS
typedef struct retro_spsc
{
uint8_t *buffer;
size_t capacity; /* power of 2; mask = capacity - 1 */
size_t capacity; /* power of 2: the most the ring holds */
/* The buffer's size less one, also a power of 2: positions wrap
* here. Equal to capacity - 1 unless the buffer is mirrored. */
size_t mask;
/* The buffer's size when its pages are mapped a second time right
* behind it, else 0: a span then runs past the end into the
* mirror, and no reservation is cut short at the wrap. */
size_t mirror;
/* Pad so head sits on a fresh cache line, isolating it from
* the buffer/capacity fields that init may touch. */
uint8_t _pad0[RETRO_SPSC_PAD0_BYTES];
@@ -185,6 +192,22 @@ typedef struct retro_spsc
*/
bool retro_spsc_init(retro_spsc_t *q, size_t min_capacity);
/**
* retro_spsc_init_mirrored:
* @q : The queue.
* @min_capacity : As retro_spsc_init; capacity is the same.
*
* As retro_spsc_init, but the buffer's pages are mapped twice, back
* to back, where the platform allows it, so write_begin and read_begin
* return every free or filled byte contiguously, across the wrap. The
* buffer is at least a page (an allocation granule on Windows); a ring
* smaller than that still holds only @min_capacity rounded up, so
* capacity, room and fill read exactly as they would from
* retro_spsc_init. Where mirroring is unavailable the queue is a plain
* one and q->mirror is 0.
*/
bool retro_spsc_init_mirrored(retro_spsc_t *q, size_t min_capacity);
/**
* retro_spsc_free:
* @q : The queue.
@@ -304,7 +327,8 @@ size_t retro_spsc_peek(const retro_spsc_t *q, void *data, size_t bytes);
*
* Producer-side zero-copy reservation. Returns the number of bytes
* that can be written CONTIGUOUSLY at *@ptr - this is min(total free
* space, distance to the physical end of the ring), so it can be
* space, distance to the physical end of the ring; on a mirrored ring
* the total free space alone), so it can be
* smaller than retro_spsc_write_avail() when the head is near the
* wrap point. No cursor moves; call retro_spsc_write_end() with the
* number of bytes actually written (which may be 0, and must not
@@ -345,8 +369,9 @@ void retro_spsc_write_end(retro_spsc_t *q, size_t bytes);
*
* Consumer-side zero-copy drain. Returns the number of bytes
* readable CONTIGUOUSLY at *@ptr - min(total buffered, distance to
* the physical end of the ring). Wrapped data needs a second
* begin/end round after consuming the first span. No cursor moves;
* the physical end of the ring; on a mirrored ring all of it).
* Otherwise wrapped data needs a second begin/end round after
* consuming the first span. No cursor moves;
* call retro_spsc_read_end() with the bytes actually consumed.
*
* The region stays stable until read_end: the producer cannot write
+247 -31
View File
@@ -20,10 +20,192 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* memfd_create goes through syscall(), and ftruncate and shm_open
* are POSIX: strict C89 builds hide all three without these. */
#if defined(__linux__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
/* Tiger must keep the legacy symbols rather than UNIX2003 variants. */
#if defined(__APPLE__) && !defined(_DARWIN_C_SOURCE) \
&& !(defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) \
&& (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1050))
#define _DARWIN_C_SOURCE
#endif
#include <stdlib.h>
#include <string.h>
#include <retro_inline.h>
#include <retro_spsc.h>
/* Where a buffer can be mapped twice, back to back. RETRO_SPSC_MIRROR_SHM
* selects the POSIX shm path on any POSIX host, for its tests. */
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
#define SPSC_MIRROR_WIN32
#elif defined(RETRO_SPSC_MIRROR_SHM)
#define SPSC_MIRROR_SHM
#elif defined(__linux__) && !defined(__EMSCRIPTEN__)
#include <sys/syscall.h>
#ifdef __NR_memfd_create
#define SPSC_MIRROR_MEMFD
#endif
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if !TARGET_OS_IPHONE
#define SPSC_MIRROR_SHM
#endif
#elif (defined(__FreeBSD__) && !defined(__ORBIS__)) || defined(__NetBSD__) \
|| defined(__OpenBSD__)
#define SPSC_MIRROR_SHM
#endif
#if defined(SPSC_MIRROR_WIN32)
#include <windows.h>
#elif defined(SPSC_MIRROR_MEMFD) || defined(SPSC_MIRROR_SHM)
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#endif
#if defined(SPSC_MIRROR_WIN32) || defined(SPSC_MIRROR_MEMFD) \
|| defined(SPSC_MIRROR_SHM)
#define SPSC_MIRROR
#endif
#ifdef SPSC_MIRROR_SHM
#include <stdio.h>
#endif
/* Bytes contiguous at @idx: to the buffer's end, or past it into the
* mirror. */
static INLINE size_t spsc_contig(const retro_spsc_t *q, size_t idx)
{
return q->mirror ? (size_t)-1 : q->mask + 1 - idx;
}
#ifdef SPSC_MIRROR
#if !defined(SPSC_MIRROR_WIN32) && !defined(MAP_ANONYMOUS)
#define MAP_ANONYMOUS MAP_ANON
#endif
/* What a mapping is made of: a page, or Windows' allocation granule. */
static size_t spsc_mirror_granule(void)
{
#ifdef SPSC_MIRROR_WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return (size_t)si.dwAllocationGranularity;
#else
long page = sysconf(_SC_PAGESIZE);
return page > 0 ? (size_t)page : 4096;
#endif
}
static void spsc_mirror_free(uint8_t *base, size_t size)
{
#ifdef SPSC_MIRROR_WIN32
UnmapViewOfFile(base);
UnmapViewOfFile(base + size);
#else
munmap(base, size * 2);
#endif
}
#ifndef SPSC_MIRROR_WIN32
/* Both views of a descriptor's pages, inside one reservation so nothing
* can land between them. Takes the descriptor. */
static uint8_t *spsc_mirror_map(int fd, size_t size)
{
uint8_t *base = NULL;
void *res;
if (ftruncate(fd, (off_t)size) == 0)
{
res = mmap(NULL, size * 2, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (res != MAP_FAILED)
{
base = (uint8_t*)res;
if ( mmap(base, size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED, fd, 0) == MAP_FAILED
|| mmap(base + size, size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED, fd, 0) == MAP_FAILED)
{
munmap(base, size * 2);
base = NULL;
}
}
}
close(fd);
return base;
}
#endif
static uint8_t *spsc_mirror_alloc(size_t size)
{
uint8_t *base = NULL;
#if defined(SPSC_MIRROR_WIN32)
unsigned tries;
HANDLE h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, (DWORD)((uint64_t)size >> 32), (DWORD)size, NULL);
if (!h)
return NULL;
/* The reservation is released before the views are placed in it,
* so another thread can take the address meanwhile: try again. */
for (tries = 0; tries < 16 && !base; tries++)
{
uint8_t *at = (uint8_t*)VirtualAlloc(NULL, size * 2, MEM_RESERVE,
PAGE_NOACCESS);
void *lo, *hi;
if (!at)
break;
VirtualFree(at, 0, MEM_RELEASE);
if (!(lo = MapViewOfFileEx(h, FILE_MAP_ALL_ACCESS, 0, 0, size, at)))
continue;
hi = MapViewOfFileEx(h, FILE_MAP_ALL_ACCESS, 0, 0, size, at + size);
if (hi == (void*)(at + size))
base = at;
else
{
if (hi)
UnmapViewOfFile(hi);
UnmapViewOfFile(lo);
}
}
CloseHandle(h);
#elif defined(SPSC_MIRROR_MEMFD)
int fd = (int)syscall(__NR_memfd_create, "retro_spsc", 1u /* MFD_CLOEXEC */);
if (fd >= 0)
base = spsc_mirror_map(fd, size);
#else
static unsigned serial;
unsigned tries;
for (tries = 0; tries < 8 && !base; tries++)
{
char name[48];
int fd;
sprintf(name, "/retro_spsc.%lu.%u", (unsigned long)getpid(),
serial++);
if ((fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0)
continue;
shm_unlink(name);
base = spsc_mirror_map(fd, size);
break;
}
#endif
/* A write through one view must read back through the other. */
if (base)
{
base[0] = 0x5a;
if (base[size] != 0x5a)
{
spsc_mirror_free(base, size);
return NULL;
}
base[0] = 0;
}
return base;
}
#endif
/* Round @v up to the next power of 2. Returns 0 if @v == 0 or if
* the next power of 2 would overflow (caller checks @v <= SIZE_MAX/2). */
static size_t spsc_round_up_pow2(size_t v)
@@ -58,6 +240,8 @@ bool retro_spsc_init(retro_spsc_t *q, size_t min_capacity)
return false;
q->capacity = cap;
q->mask = cap - 1;
q->mirror = 0;
q->cached_tail = 0;
q->cached_head = 0;
retro_atomic_size_init(&q->head, 0);
@@ -65,16 +249,53 @@ bool retro_spsc_init(retro_spsc_t *q, size_t min_capacity)
return true;
}
bool retro_spsc_init_mirrored(retro_spsc_t *q, size_t min_capacity)
{
#ifdef SPSC_MIRROR
size_t cap, size, granule;
uint8_t *buf;
if (!q || min_capacity == 0 || min_capacity > (SIZE_MAX / 2))
return false;
cap = spsc_round_up_pow2(min_capacity);
granule = spsc_mirror_granule();
/* Positions wrap at the buffer's size, so it has to be a power of
* 2 as well: the capacity, or the granule where that is larger. */
size = cap > granule ? cap : granule;
if ( cap && !(granule & (granule - 1))
&& size <= (SIZE_MAX / 4)
&& (buf = spsc_mirror_alloc(size)))
{
q->buffer = buf;
q->capacity = cap;
q->mask = size - 1;
q->mirror = size;
q->cached_tail = 0;
q->cached_head = 0;
retro_atomic_size_init(&q->head, 0);
retro_atomic_size_init(&q->tail, 0);
return true;
}
#endif
return retro_spsc_init(q, min_capacity);
}
void retro_spsc_free(retro_spsc_t *q)
{
if (!q)
return;
if (q->buffer)
{
free(q->buffer);
#ifdef SPSC_MIRROR
if (q->mirror)
spsc_mirror_free(q->buffer, q->mirror);
else
#endif
free(q->buffer);
q->buffer = NULL;
}
q->capacity = 0;
q->mirror = 0;
}
void retro_spsc_clear(retro_spsc_t *q)
@@ -130,7 +351,7 @@ size_t retro_spsc_read_avail(const retro_spsc_t *q)
size_t retro_spsc_write(retro_spsc_t *q, const void *data, size_t bytes)
{
size_t mask, head_idx, first;
size_t head_idx, first;
const uint8_t *src = (const uint8_t*)data;
/* head is ours (relaxed). Room is first computed from our private
* copy of tail, which is never ahead of the real one; only when it
@@ -149,11 +370,10 @@ size_t retro_spsc_write(retro_spsc_t *q, const void *data, size_t bytes)
if (bytes == 0)
return 0;
mask = q->capacity - 1;
head_idx = head & mask;
head_idx = head & q->mask;
/* first = bytes from head_idx to end-of-buffer */
first = q->capacity - head_idx;
first = spsc_contig(q, head_idx);
if (first > bytes)
first = bytes;
@@ -183,8 +403,8 @@ size_t retro_spsc_write_frames(retro_spsc_t *q, const void *data,
if (!frames) return 0;
/* Multiplication is bounded by capacity, even for an oversized request. */
bytes = frames * frame_bytes;
index = head & (q->capacity - 1);
first = q->capacity - index;
index = head & q->mask;
first = spsc_contig(q, index);
if (first > bytes) first = bytes;
memcpy(q->buffer + index, src, first);
memcpy(q->buffer, src + first, bytes - first);
@@ -194,7 +414,7 @@ size_t retro_spsc_write_frames(retro_spsc_t *q, const void *data,
size_t retro_spsc_read(retro_spsc_t *q, void *data, size_t bytes)
{
size_t mask, tail_idx, first;
size_t tail_idx, first;
uint8_t *dst = (uint8_t*)data;
/* acquire on head pairs with producer's release-store; this is
* what makes the subsequent memcpys safe to read. */
@@ -214,10 +434,9 @@ size_t retro_spsc_read(retro_spsc_t *q, void *data, size_t bytes)
if (bytes == 0)
return 0;
mask = q->capacity - 1;
tail_idx = tail & mask;
tail_idx = tail & q->mask;
first = q->capacity - tail_idx;
first = spsc_contig(q, tail_idx);
if (first > bytes)
first = bytes;
@@ -231,7 +450,7 @@ size_t retro_spsc_read(retro_spsc_t *q, void *data, size_t bytes)
size_t retro_spsc_peek(const retro_spsc_t *q, void *data, size_t bytes)
{
size_t mask, tail_idx, first;
size_t tail_idx, first;
uint8_t *dst = (uint8_t*)data;
/* Consumer side, same scheme as retro_spsc_read; the cast is only
* because peek takes a const queue and the cached copy is state. */
@@ -248,10 +467,9 @@ size_t retro_spsc_peek(const retro_spsc_t *q, void *data, size_t bytes)
if (bytes == 0)
return 0;
mask = q->capacity - 1;
tail_idx = tail & mask;
tail_idx = tail & q->mask;
first = q->capacity - tail_idx;
first = spsc_contig(q, tail_idx);
if (first > bytes)
first = bytes;
@@ -263,21 +481,20 @@ size_t retro_spsc_peek(const retro_spsc_t *q, void *data, size_t bytes)
size_t retro_spsc_write_begin(retro_spsc_t *q, void **ptr)
{
size_t mask, head_idx, span;
size_t head_idx, span;
/* head is ours (relaxed). Room from the private copy of tail
* first; re-read the consumer's tail only when that says none.
* See retro_spsc_write. */
* first; re-read the consumer's tail only when that copy, rather
* than the wrap, is what limits the span - a stale copy would hand
* back less than is free. See retro_spsc_write. */
size_t head = retro_atomic_load_relaxed_size(&q->head);
size_t avail = q->capacity - (head - q->cached_tail);
if (avail == 0)
head_idx = head & q->mask;
span = spsc_contig(q, head_idx);
if (avail < span && avail < q->capacity)
{
q->cached_tail = retro_atomic_load_acquire_size(&q->tail);
avail = q->capacity - (head - q->cached_tail);
}
mask = q->capacity - 1;
head_idx = head & mask;
span = q->capacity - head_idx;
if (span > avail)
span = avail;
*ptr = q->buffer + head_idx;
@@ -301,21 +518,20 @@ void retro_spsc_write_end(retro_spsc_t *q, size_t bytes)
size_t retro_spsc_read_begin(retro_spsc_t *q, const void **ptr)
{
size_t mask, tail_idx, span;
size_t tail_idx, span;
/* tail is ours (relaxed). Available from the private copy of
* head first; re-read the producer's head only when that says
* none. See retro_spsc_read. */
* head first; re-read the producer's head only when that copy,
* rather than the wrap, is what limits the span. See
* retro_spsc_read. */
size_t tail = retro_atomic_load_relaxed_size(&q->tail);
size_t avail = q->cached_head - tail;
if (avail == 0)
tail_idx = tail & q->mask;
span = spsc_contig(q, tail_idx);
if (avail < span && avail < q->capacity)
{
q->cached_head = retro_atomic_load_acquire_size(&q->head);
avail = q->cached_head - tail;
}
mask = q->capacity - 1;
tail_idx = tail & mask;
span = q->capacity - tail_idx;
if (span > avail)
span = avail;
*ptr = q->buffer + tail_idx;
+36
View File
@@ -0,0 +1,36 @@
include ../../sample_objdir.mk
TARGET := ratrac3_test
LIBRETRO_COMM_DIR := ../../..
SOURCES := \
ratrac3_test.c \
$(LIBRETRO_COMM_DIR)/formats/atrac3/ratrac3.c
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
CFLAGS += -Wall -pedantic -std=c89 -g -O1 -I$(LIBRETRO_COMM_DIR)/include
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS) -lm
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: clean
+341
View File
@@ -0,0 +1,341 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (ratrac3_test.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* 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 OR COPYRIGHT HOLDERS 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.
*/
/* ratrac3 against a reference decoder's output for two crafted streams:
* a stereo one (gain control, tonal components in all their codings,
* the band limits, fewer coded bands, an empty unit) and a joint stereo
* one (every matrix and weight change, padding, side units of one to
* four bands). Then the s16 output, the fmt chunk, bad frames, reset,
* and garbage. */
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <formats/ratrac3.h>
#include "ratrac3_test_data.h"
static int failures = 0;
static void CHECK(int cond, const char *fmt, ...)
{
va_list ap;
if (cond)
return;
va_start(ap, fmt);
printf("FAIL: ");
vprintf(fmt, ap);
printf("\n");
va_end(ap);
failures++;
}
#define FRAMES 12
static double compare(const uint8_t *stream, unsigned block_align,
bool joint, const float (*ref)[2], unsigned nref, const char *name)
{
static float pcm[FRAMES * RATRAC3_SAMPLES * 2];
ratrac3_t *d = ratrac3_new(2, block_align, joint);
unsigned f, i;
double err = 0.0, sig = 0.0, worst = 0.0;
CHECK(d != NULL, "%s: new", name);
if (!d)
return 0.0;
for (f = 0; f < FRAMES; f++)
{
int r = ratrac3_decode_float(d, stream + f * block_align, block_align,
pcm + f * RATRAC3_SAMPLES * 2);
CHECK(r == RATRAC3_SAMPLES, "%s: frame %u returned %d", name, f, r);
}
for (i = 0; i < nref; i++)
{
unsigned c;
for (c = 0; c < 2; c++)
{
double a = pcm[i * STREAM_STEP * 2 + c], b = ref[i][c];
double e = fabs(a - b);
err += (a - b) * (a - b);
sig += b * b;
if (e > worst)
worst = e;
}
}
ratrac3_free(d);
CHECK(worst < 2e-5, "%s: worst sample error %g", name, worst);
return 10.0 * log10(sig / (err > 0.0 ? err : 1e-30));
}
static void lane_reference(void)
{
double a = compare(stream_a, 384, false, ref_a,
sizeof(ref_a) / sizeof(ref_a[0]), "stereo");
double b = compare(stream_b, 192, true, ref_b,
sizeof(ref_b) / sizeof(ref_b[0]), "joint stereo");
printf("stereo %.1f dB, joint stereo %.1f dB against the reference\n",
a, b);
CHECK(a > 100.0 && b > 100.0, "reference match");
}
/* s16 is the float output times 32768, rounded and clamped */
static void lane_s16(void)
{
static float pf[RATRAC3_SAMPLES * 2];
static int16_t ps[RATRAC3_SAMPLES * 2];
ratrac3_t *df = ratrac3_new(2, 384, false);
ratrac3_t *ds = ratrac3_new(2, 384, false);
unsigned f, i, clipped = 0, bad = 0;
for (f = 0; f < FRAMES; f++)
{
ratrac3_decode_float(df, stream_a + f * 384, 384, pf);
ratrac3_decode_s16(ds, stream_a + f * 384, 384, ps);
for (i = 0; i < RATRAC3_SAMPLES * 2; i++)
{
double v = pf[i] * 32768.0;
long e;
if (v >= 32767.0)
{
e = 32767;
clipped++;
}
else if (v <= -32768.0)
{
e = -32768;
clipped++;
}
else
e = (long)(v < 0 ? v - 0.5 : v + 0.5);
if (labs(e - ps[i]) > 1)
bad++;
}
}
CHECK(bad == 0, "s16: %u samples off", bad);
CHECK(clipped > 0, "s16: the stereo stream should clip somewhere");
ratrac3_free(df);
ratrac3_free(ds);
}
static void put16(uint8_t *p, unsigned v)
{
p[0] = (uint8_t)v;
p[1] = (uint8_t)(v >> 8);
}
static void put32(uint8_t *p, unsigned long v)
{
put16(p, (unsigned)(v & 0xFFFF));
put16(p + 2, (unsigned)(v >> 16));
}
static void make_fmt(uint8_t *fmt, unsigned tag, unsigned ch,
unsigned ba, unsigned mode)
{
memset(fmt, 0, 32);
put16(fmt, tag);
put16(fmt + 2, ch);
put32(fmt + 4, 44100);
put32(fmt + 8, (unsigned long)ba * 44100 / 1024);
put16(fmt + 12, ba);
put16(fmt + 16, 14);
put16(fmt + 18, 1);
put32(fmt + 20, 0x1000);
put16(fmt + 24, mode);
put16(fmt + 26, mode);
put16(fmt + 28, 1);
}
static void lane_fmt(void)
{
uint8_t fmt[32];
ratrac3_info_t info;
make_fmt(fmt, 0x270, 2, 192, 1);
CHECK(ratrac3_parse_wave_fmt(fmt, 32, &info), "fmt: joint");
CHECK(info.channels == 2 && info.block_align == 192 && info.joint_stereo
&& info.sample_rate == 44100, "fmt: joint fields");
CHECK(info.bitrate == 192u * 44100 / 1024 * 8, "fmt: bitrate %u",
info.bitrate);
make_fmt(fmt, 0x270, 2, 384, 0);
CHECK(ratrac3_parse_wave_fmt(fmt, 32, &info) && !info.joint_stereo
&& info.block_align == 384, "fmt: stereo");
make_fmt(fmt, 0x270, 1, 192, 0);
CHECK(ratrac3_parse_wave_fmt(fmt, 32, &info) && info.channels == 1,
"fmt: mono");
/* No extra data: not joint */
CHECK(ratrac3_parse_wave_fmt(fmt, 16, &info) && !info.joint_stereo,
"fmt: bare");
make_fmt(fmt, 0xFFFE, 2, 384, 0);
CHECK(!ratrac3_parse_wave_fmt(fmt, 32, &info), "fmt: other tag");
make_fmt(fmt, 0x270, 1, 192, 1);
CHECK(!ratrac3_parse_wave_fmt(fmt, 32, &info), "fmt: mono joint");
make_fmt(fmt, 0x270, 2, 191, 0);
CHECK(!ratrac3_parse_wave_fmt(fmt, 32, &info), "fmt: odd block");
make_fmt(fmt, 0x270, 3, 384, 0);
CHECK(!ratrac3_parse_wave_fmt(fmt, 32, &info), "fmt: three channels");
CHECK(!ratrac3_parse_wave_fmt(fmt, 15, &info), "fmt: short");
CHECK(!ratrac3_parse_wave_fmt(NULL, 32, &info), "fmt: NULL");
CHECK(ratrac3_new(0, 384, false) == NULL, "new: no channels");
CHECK(ratrac3_new(3, 384, false) == NULL, "new: three channels");
CHECK(ratrac3_new(2, 0, false) == NULL, "new: no block");
CHECK(ratrac3_new(2, 383, false) == NULL, "new: odd block");
CHECK(ratrac3_new(1, 192, true) == NULL, "new: mono joint");
}
/* A frame that does not parse gives silence and -1 and leaves the
* history alone: what follows decodes as if it were not there */
static void lane_bad_frame(void)
{
static float a[RATRAC3_SAMPLES * 2], b[RATRAC3_SAMPLES * 2];
unsigned pass;
for (pass = 0; pass < 2; pass++)
{
const uint8_t *s = pass ? stream_b : stream_a;
unsigned ba = pass ? 192 : 384;
unsigned at = pass ? 3 : 4;
ratrac3_t *d1 = ratrac3_new(2, ba, pass != 0);
ratrac3_t *d2 = ratrac3_new(2, ba, pass != 0);
uint8_t bad[384];
unsigned f, i;
int r;
memcpy(bad, s + at * ba, ba);
if (pass)
bad[ba - 2] &= 0xF3; /* the side unit's id, after the weights */
else
bad[0] ^= 0x80; /* the unit id */
for (f = 0; f < FRAMES; f++)
{
if (f == at)
{
r = ratrac3_decode_float(d2, bad, ba, b);
CHECK(r == -1, "bad frame: returned %d", r);
for (i = 0; i < RATRAC3_SAMPLES * 2; i++)
if (b[i] != 0.0f)
break;
CHECK(i == RATRAC3_SAMPLES * 2, "bad frame: not silent");
continue;
}
ratrac3_decode_float(d1, s + f * ba, ba, a);
ratrac3_decode_float(d2, s + f * ba, ba, b);
CHECK(!memcmp(a, b, sizeof(a)), "bad frame: frame %u differs", f);
}
/* A short buffer is a bad frame */
r = ratrac3_decode_float(d1, s, ba - 1, a);
CHECK(r == -1, "short frame: returned %d", r);
r = ratrac3_decode_float(d1, NULL, ba, a);
CHECK(r == -1, "NULL frame: returned %d", r);
ratrac3_free(d1);
ratrac3_free(d2);
}
}
static void lane_reset(void)
{
static float a[RATRAC3_SAMPLES * 2], b[RATRAC3_SAMPLES * 2];
ratrac3_t *d1 = ratrac3_new(2, 192, true);
ratrac3_t *d2 = ratrac3_new(2, 192, true);
unsigned f;
for (f = 0; f < FRAMES; f++)
ratrac3_decode_float(d1, stream_b + f * 192, 192, a);
ratrac3_reset(d1);
for (f = 0; f < FRAMES; f++)
{
ratrac3_decode_float(d1, stream_b + f * 192, 192, a);
ratrac3_decode_float(d2, stream_b + f * 192, 192, b);
CHECK(!memcmp(a, b, sizeof(a)), "reset: frame %u differs", f);
}
ratrac3_free(d1);
ratrac3_free(d2);
}
/* Garbage and mutations must not read or write out of bounds (run
* under a sanitizer) and must keep the output finite */
static unsigned long rng_state = 12345;
static unsigned rng(void)
{
rng_state = rng_state * 1103515245UL + 12345UL;
return (unsigned)(rng_state >> 16) & 0x7FFF;
}
static void lane_garbage(void)
{
static float pcm[RATRAC3_SAMPLES * 2];
unsigned mode;
for (mode = 0; mode < 3; mode++)
{
unsigned ba = mode == 1 ? 192 : 384, ch = mode == 2 ? 1 : 2;
ratrac3_t *d = ratrac3_new(ch, ba, mode == 1);
unsigned n, ok = 0, nonfinite = 0;
for (n = 0; n < 4000; n++)
{
uint8_t frame[384];
unsigned i, k = rng() % 3;
if (k == 0)
for (i = 0; i < ba; i++)
frame[i] = (uint8_t)rng();
else
{
const uint8_t *src = mode == 1 ? stream_b + (n % FRAMES) * 192
: stream_a + (n % FRAMES) * 384;
memcpy(frame, src, ba);
for (i = 0; i < 1 + rng() % 8; i++)
frame[rng() % ba] ^= (uint8_t)(1u << (rng() % 8));
if (k == 2)
frame[0] = (uint8_t)(0xA0 | (frame[0] & 3));
}
if (ratrac3_decode_float(d, frame, ba, pcm) > 0)
ok++;
for (i = 0; i < RATRAC3_SAMPLES * ch; i++)
if (!(pcm[i] == pcm[i]) || fabs(pcm[i]) > 1e30)
nonfinite++;
}
CHECK(nonfinite == 0, "garbage: %u non-finite samples", nonfinite);
printf("garbage mode %u: %u of 4000 frames decoded\n", mode, ok);
ratrac3_free(d);
}
}
int main(void)
{
lane_reference();
lane_s16();
lane_fmt();
lane_bad_frame();
lane_reset();
lane_garbage();
if (failures)
{
printf("%d failure(s)\n", failures);
return 1;
}
printf("ratrac3: all tests passed\n");
return 0;
}
+536
View File
@@ -0,0 +1,536 @@
/* Generated: crafted ATRAC3 frames and a reference decoder's output
* for them, sign flipped to this decoder's polarity. */
#define STREAM_STEP 97
static const uint8_t stream_a[4608] = {
0xa3,0x00,0x00,0x2f,0xa4,0x9b,0xdb,0xf4,0xf0,0x29,0x3b,0x72,0xc3,0x34,0xec,0xb2,
0xcc,0x40,0xce,0xa5,0x38,0x81,0x40,0x00,0x18,0x78,0x00,0x1e,0x40,0x06,0x40,0x21,
0xc0,0x20,0x13,0x00,0x00,0x00,0x2c,0x0c,0xda,0x03,0x80,0x00,0x4c,0xf2,0x0a,0x9e,
0x1e,0x01,0x34,0xa0,0x0a,0xe0,0x04,0x57,0x80,0x36,0x00,0x00,0x1f,0x60,0x00,0x44,
0x00,0x09,0x02,0xc4,0x80,0x00,0x40,0xc8,0x3a,0x00,0x00,0x88,0x40,0x07,0xb8,0x1e,
0x01,0xff,0xfc,0x04,0x2e,0x06,0x02,0x01,0x39,0x60,0x1c,0xa5,0x60,0x00,0xc8,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x00,0x00,0x2f,0x23,0x7f,0xfc,0x56,0xcf,0x46,0xf2,0xab,0xb3,0x22,0xf2,0x34,
0xb3,0x20,0x81,0xa0,0x9c,0x00,0x1a,0x01,0xbf,0xe3,0x2d,0xc7,0x00,0xa4,0x0a,0xc1,
0xa6,0x00,0x00,0x4d,0x72,0xc0,0x00,0x60,0x0b,0x00,0xf3,0xa0,0x30,0x01,0x40,0x00,
0x02,0x20,0xe8,0x22,0x40,0x0b,0xd8,0x06,0x3e,0x00,0x07,0x80,0x02,0x20,0x90,0x40,
0x4f,0x38,0x00,0xd0,0x3b,0x00,0x58,0x00,0xf0,0x00,0x80,0x43,0x3c,0x0b,0x80,0x00,
0x00,0x01,0xe0,0x77,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x46,0x55,0xa0,0x84,0x00,0x38,0xd4,0xfc,0x01,0x32,0xbd,0x10,0x04,0x00,0x20,
0x04,0x1d,0x3d,0x3b,0x6e,0xcf,0x3c,0x2f,0x2a,0xb1,0x96,0x9e,0xd7,0xe3,0x00,0x5e,
0x00,0x1e,0x60,0x9c,0xfc,0x6a,0xe8,0xcd,0xd7,0xed,0x60,0x1b,0x14,0x0e,0x00,0xe7,
0xf8,0x74,0x94,0xa7,0xe8,0x56,0x56,0x3c,0xc0,0x94,0x5d,0xea,0x06,0x40,0x30,0x00,
0x80,0x04,0x99,0x50,0x00,0xd0,0x2b,0x0c,0x20,0x39,0x83,0xc9,0x86,0xec,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x46,0x55,0xa0,0x84,0x00,0x38,0xd4,0xfc,0x01,0x37,0x67,0x40,0x07,0x00,0x50,
0x07,0x15,0x40,0xcb,0x51,0x38,0xfa,0xcd,0xa8,0xa8,0x30,0x00,0xeb,0xf2,0x37,0x5a,
0x01,0xc8,0xea,0xe3,0xf0,0x7e,0x80,0x00,0x3c,0x04,0xa1,0x80,0x3b,0xc8,0x0d,0x00,
0x01,0xe8,0x10,0x00,0x68,0x58,0xac,0x79,0xf2,0xb0,0x34,0x79,0x0d,0x00,0x00,0x00,
0xcc,0x0f,0x79,0x7f,0xfa,0x81,0x98,0x5c,0x00,0x24,0x30,0x02,0x8f,0xed,0x1a,0x00,
0x00,0x19,0x00,0x05,0x07,0x80,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x05,0xbe,0x00,0x8c,0x70,0x17,0x83,0xc5,0x80,0x00,0x5c,0xa2,0x40,0x41,0x60,
0x02,0xcf,0xfd,0xb2,0x2a,0xc0,0x60,0x00,0x00,0x00,0x00,0xdf,0x89,0xa9,0x1c,0x47,
0x03,0x43,0x66,0x81,0xa2,0x67,0x7f,0x34,0x68,0xa5,0xcc,0x8d,0x78,0x6b,0xcb,0x3d,
0x9e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x05,0xbe,0x00,0x8c,0x70,0x17,0x83,0xc5,0x80,0x00,0x5c,0xa2,0x40,0x41,0x60,
0x02,0xcf,0xfd,0xb2,0x14,0xc0,0xe0,0x00,0x00,0x00,0x00,0x5c,0x79,0x57,0x65,0xaf,
0x3f,0x5a,0x46,0x96,0x66,0xeb,0x1f,0x6c,0xdb,0x40,0x0f,0x7b,0x1b,0x3b,0xb6,0xcc,
0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x00,0x01,0x70,0xec,0x00,0x61,0xf5,0x64,0xc4,0xd6,0x15,0x05,0xe2,0x50,0x00,
0x00,0x2d,0x64,0x41,0xef,0x24,0x90,0x00,0x00,0x80,0x00,0x00,0x80,0x02,0x8d,0x95,
0xd4,0x69,0xe8,0x4f,0xc3,0x1c,0x00,0x01,0xf0,0x11,0x0c,0x30,0x00,0x03,0x00,0x03,
0xc7,0x40,0x01,0x40,0x00,0x44,0x5c,0x3f,0x00,0x00,0x00,0x00,0x0d,0xc6,0xf8,0x00,
0x00,0xe1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x00,0x01,0x70,0xec,0x00,0x61,0xf5,0x64,0xc4,0xd6,0x15,0x05,0xe2,0x50,0x00,
0x00,0x2d,0x64,0x41,0xef,0x24,0x90,0x00,0x00,0x80,0x00,0x00,0x80,0x05,0x69,0x55,
0x23,0x52,0x35,0x50,0x00,0x40,0x03,0x03,0x03,0xc4,0x40,0x30,0x0c,0x77,0x4c,0xc7,
0x03,0x54,0xfc,0x31,0x00,0x07,0x40,0x02,0x40,0x11,0x02,0x70,0x00,0x0f,0xc0,0x24,
0x81,0x40,0x03,0xc8,0x00,0x43,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x00,0x00,0x69,0xe7,0xc0,0x00,0x08,0x00,0x00,0x08,0x00,0x01,0x44,0x76,0x49,
0xe7,0x75,0xd5,0x21,0x1e,0xc3,0x78,0x00,0x3b,0x87,0xfa,0xf0,0x00,0x00,0x39,0x0d,
0xe6,0x80,0x01,0x1f,0xb7,0x06,0x90,0xd3,0x4d,0x68,0x02,0x01,0x00,0x00,0xe4,0x00,
0x00,0x34,0x0d,0x3b,0xe0,0x02,0x00,0x0f,0xa0,0xf1,0xc3,0xdb,0xe3,0xc4,0x7c,0x17,
0xac,0x8f,0xff,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x00,0x00,0x69,0xd6,0x60,0x00,0x10,0x00,0x00,0x10,0x00,0x00,0xc5,0x55,0x69,
0x5b,0x95,0x47,0x5c,0x00,0x5e,0x3e,0xc0,0x61,0x5f,0xf0,0x0a,0x29,0x20,0x8c,0x34,
0xa0,0x00,0x91,0xc0,0x75,0xd4,0x37,0x03,0x00,0x00,0x34,0x0a,0x3c,0x00,0x00,0x0c,
0x0f,0x8c,0x00,0xfc,0x08,0x3d,0x03,0x48,0x37,0x97,0xd0,0xab,0x00,0xfc,0x36,0xc9,
0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa1,0x28,0x35,0x2a,0x25,0x83,0xd9,0x40,0x02,0x80,0x00,0x00,0x00,0x00,0x00,0x00,
0x08,0x00,0xab,0x23,0x71,0xb2,0x1e,0x00,0x18,0x00,0x00,0x00,0x61,0x80,0x00,0x00,
0x00,0x80,0x00,0x00,0x00,0x60,0x80,0x00,0x01,0x80,0x00,0x00,0x08,0x00,0x00,0x06,
0x00,0x0a,0x00,0x00,0x00,0x00,0x80,0x7e,0x00,0x08,0x80,0x00,0x06,0x0a,0x18,0x08,
0x00,0x20,0x00,0x00,0x00,0x22,0x88,0x00,0x61,0x80,0x00,0x18,0x06,0x00,0x38,0x60,
0x00,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa1,0x28,0x35,0x2a,0x25,0x83,0xd3,0x40,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x38,0x03,0xb1,0x2c,0xca,0x28,0x20,0x00,0x1e,0x01,0xa0,0x01,0x40,0x01,0x20,0x00,
0x00,0x00,0x08,0x80,0x00,0x1e,0x6a,0x01,0xc0,0x30,0x00,0x00,0x00,0x03,0x00,0x00,
0x06,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x00,0x38,0x00,0x18,0x00,0x00,
0x1f,0x00,0x00,0x0c,0x38,0x00,0x16,0x9c,0x00,0x00,0x00,0x32,0x60,0x01,0x30,0x00,
0x68,0x00,0xe0,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x01,0xd0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0x00,0x00,0x04,
0x60,0x00,0x00,0x19,0xee,0x68,0x09,0x4e,0x00,0x00,0x00,0xa9,0x80,0x11,0xb8,0x00,
0x19,0xa8,0x11,0x80,0x00,0x60,0x01,0x60,0x00,0x00,0x04,0x21,0x01,0x80,0x00,0x55,
0xf0,0x00,0x00,0x00,0x00,0x78,0xeb,0x00,0x00,0x00,0x00,0x00,0x71,0x17,0x00,0x00,
0x00,0x7a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6e,0x00,0x00,0x00,0x00,0x1a,
0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa0,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa0,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x5e,0x10,0x10,0x08,0x01,0xff,0x07,0xda,0xaa,0x00,0x00,0x00,0x00,0x00,0x38,
0x00,0x00,0x10,0x00,0xee,0xb1,0x13,0x31,0xb1,0x2c,0xe0,0x00,0x00,0x42,0x4a,0x30,
0x01,0x4f,0x34,0x24,0x00,0x78,0x7e,0x9c,0x00,0x1e,0x30,0x8b,0xe0,0x64,0x0a,0x43,
0xa7,0x81,0x80,0x94,0x8a,0x2b,0x86,0x50,0x1b,0xa0,0x09,0x80,0xfd,0x80,0xbc,0x0e,
0x60,0x00,0x00,0x00,0x0f,0x8d,0xc0,0x0c,0x40,0x03,0xd8,0x00,0x00,0x9c,0x77,0x80,
0x07,0x50,0x08,0x00,0x00,0x00,0x00,0x29,0x00,0x00,0x8a,0x43,0x27,0x00,0x00,0x24,
0x80,0x13,0x00,0x3d,0xc7,0xcf,0xef,0xf0,0x00,0x00,0x00,0x03,0x70,0xdb,0xfe,0x00,
0x00,0x0c,0xac,0xf6,0xee,0x00,0x0a,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x5e,0x10,0x10,0x08,0x01,0xff,0x07,0xde,0x54,0x00,0x00,0x00,0x00,0x00,0x72,
0x00,0x00,0x30,0x00,0x91,0xc7,0x2a,0xce,0x3f,0x04,0xc0,0x0a,0x00,0x02,0x3c,0x70,
0x08,0x00,0x00,0x00,0x01,0x80,0x00,0x40,0x5a,0x07,0xe7,0xa8,0x0b,0x48,0x00,0x00,
0x00,0x1c,0x40,0x00,0xa0,0xe0,0x00,0x05,0xe2,0x80,0x00,0x07,0x00,0x00,0x76,0x00,
0xf8,0x02,0x05,0xe5,0x00,0x0f,0xb3,0xf4,0x90,0x51,0xfc,0x70,0x06,0x81,0xf3,0xc0,
0x04,0x34,0x08,0xd2,0x78,0x02,0xe0,0x11,0x9a,0x66,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x46,0x55,0xa0,0x84,0x00,0x38,0xd4,0xfc,0x01,0x32,0xbd,0x10,0x04,0x00,0x20,
0x04,0x1d,0x3d,0x3b,0x6e,0xcf,0x3c,0x2f,0x2a,0xb1,0x96,0x9e,0xd7,0xe3,0x00,0x5e,
0x00,0x1e,0x60,0x9c,0xfc,0x6a,0xe8,0xcd,0xd7,0xed,0x60,0x1b,0x14,0x0e,0x00,0xe7,
0xf8,0x74,0x94,0xa7,0xe8,0x56,0x56,0x3c,0xc0,0x94,0x5d,0xea,0x06,0x40,0x30,0x00,
0x80,0x04,0x99,0x50,0x00,0xd0,0x2b,0x0c,0x20,0x39,0x83,0xc9,0x86,0xec,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x46,0x55,0xa0,0x84,0x00,0x38,0xd4,0xfc,0x01,0x37,0x67,0x40,0x07,0x00,0x50,
0x07,0x15,0x40,0xcb,0x51,0x38,0xfa,0xcd,0xa8,0xa8,0x30,0x00,0xeb,0xf2,0x37,0x5a,
0x01,0xc8,0xea,0xe3,0xf0,0x7e,0x80,0x00,0x3c,0x04,0xa1,0x80,0x3b,0xc8,0x0d,0x00,
0x01,0xe8,0x10,0x00,0x68,0x58,0xac,0x79,0xf2,0xb0,0x34,0x79,0x0d,0x00,0x00,0x00,
0xcc,0x0f,0x79,0x7f,0xfa,0x81,0x98,0x5c,0x00,0x24,0x30,0x02,0x8f,0xed,0x1a,0x00,
0x00,0x19,0x00,0x05,0x07,0x80,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x00,0x01,0x70,0xec,0x00,0x61,0xf5,0x64,0xc4,0xd6,0x15,0x05,0xe2,0x50,0x00,
0x00,0x2d,0x64,0x41,0xef,0x24,0x90,0x00,0x00,0x80,0x00,0x00,0x80,0x02,0x8d,0x95,
0xd4,0x69,0xe8,0x4f,0xc3,0x1c,0x00,0x01,0xf0,0x11,0x0c,0x30,0x00,0x03,0x00,0x03,
0xc7,0x40,0x01,0x40,0x00,0x44,0x5c,0x3f,0x00,0x00,0x00,0x00,0x0d,0xc6,0xf8,0x00,
0x00,0xe1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x00,0x01,0x70,0xec,0x00,0x61,0xf5,0x64,0xc4,0xd6,0x15,0x05,0xe2,0x50,0x00,
0x00,0x2d,0x64,0x41,0xef,0x24,0x90,0x00,0x00,0x80,0x00,0x00,0x80,0x05,0x69,0x55,
0x23,0x52,0x35,0x50,0x00,0x40,0x03,0x03,0x03,0xc4,0x40,0x30,0x0c,0x77,0x4c,0xc7,
0x03,0x54,0xfc,0x31,0x00,0x07,0x40,0x02,0x40,0x11,0x02,0x70,0x00,0x0f,0xc0,0x24,
0x81,0x40,0x03,0xc8,0x00,0x43,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x00,0x00,0x2f,0xa4,0x9b,0xdb,0xf4,0xf0,0x29,0x3b,0x72,0xc3,0x34,0xec,0xb2,
0xcc,0x40,0xce,0xa5,0x38,0x81,0x40,0x00,0x18,0x78,0x00,0x1e,0x40,0x06,0x40,0x21,
0xc0,0x20,0x13,0x00,0x00,0x00,0x2c,0x0c,0xda,0x03,0x80,0x00,0x4c,0xf2,0x0a,0x9e,
0x1e,0x01,0x34,0xa0,0x0a,0xe0,0x04,0x57,0x80,0x36,0x00,0x00,0x1f,0x60,0x00,0x44,
0x00,0x09,0x02,0xc4,0x80,0x00,0x40,0xc8,0x3a,0x00,0x00,0x88,0x40,0x07,0xb8,0x1e,
0x01,0xff,0xfc,0x04,0x2e,0x06,0x02,0x01,0x39,0x60,0x1c,0xa5,0x60,0x00,0xc8,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x00,0x00,0x2f,0x23,0x7f,0xfc,0x56,0xcf,0x46,0xf2,0xab,0xb3,0x22,0xf2,0x34,
0xb3,0x20,0x81,0xa0,0x9c,0x00,0x1a,0x01,0xbf,0xe3,0x2d,0xc7,0x00,0xa4,0x0a,0xc1,
0xa6,0x00,0x00,0x4d,0x72,0xc0,0x00,0x60,0x0b,0x00,0xf3,0xa0,0x30,0x01,0x40,0x00,
0x02,0x20,0xe8,0x22,0x40,0x0b,0xd8,0x06,0x3e,0x00,0x07,0x80,0x02,0x20,0x90,0x40,
0x4f,0x38,0x00,0xd0,0x3b,0x00,0x58,0x00,0xf0,0x00,0x80,0x43,0x3c,0x0b,0x80,0x00,
0x00,0x01,0xe0,0x77,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x5e,0x10,0x10,0x08,0x01,0xff,0x07,0xda,0xaa,0x00,0x00,0x00,0x00,0x00,0x38,
0x00,0x00,0x10,0x00,0xee,0xb1,0x13,0x31,0xb1,0x2c,0xe0,0x00,0x00,0x42,0x4a,0x30,
0x01,0x4f,0x34,0x24,0x00,0x78,0x7e,0x9c,0x00,0x1e,0x30,0x8b,0xe0,0x64,0x0a,0x43,
0xa7,0x81,0x80,0x94,0x8a,0x2b,0x86,0x50,0x1b,0xa0,0x09,0x80,0xfd,0x80,0xbc,0x0e,
0x60,0x00,0x00,0x00,0x0f,0x8d,0xc0,0x0c,0x40,0x03,0xd8,0x00,0x00,0x9c,0x77,0x80,
0x07,0x50,0x08,0x00,0x00,0x00,0x00,0x29,0x00,0x00,0x8a,0x43,0x27,0x00,0x00,0x24,
0x80,0x13,0x00,0x3d,0xc7,0xcf,0xef,0xf0,0x00,0x00,0x00,0x03,0x70,0xdb,0xfe,0x00,
0x00,0x0c,0xac,0xf6,0xee,0x00,0x0a,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa3,0x5e,0x10,0x10,0x08,0x01,0xff,0x07,0xde,0x54,0x00,0x00,0x00,0x00,0x00,0x72,
0x00,0x00,0x30,0x00,0x91,0xc7,0x2a,0xce,0x3f,0x04,0xc0,0x0a,0x00,0x02,0x3c,0x70,
0x08,0x00,0x00,0x00,0x01,0x80,0x00,0x40,0x5a,0x07,0xe7,0xa8,0x0b,0x48,0x00,0x00,
0x00,0x1c,0x40,0x00,0xa0,0xe0,0x00,0x05,0xe2,0x80,0x00,0x07,0x00,0x00,0x76,0x00,
0xf8,0x02,0x05,0xe5,0x00,0x0f,0xb3,0xf4,0x90,0x51,0xfc,0x70,0x06,0x81,0xf3,0xc0,
0x04,0x34,0x08,0xd2,0x78,0x02,0xe0,0x11,0x9a,0x66,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
/* every 97th sample, left then right */
static const float ref_a[127][2] = {
{ 1.126531982e-16f, 5.703195257e-17f }, { 6.125071650e-06f, 1.233613489e-06f }, { -6.888474454e-04f, -1.230235939e-04f },
{ 5.519068800e-04f, -8.532244829e-04f }, { 1.288989442e-04f, 1.366461394e-03f }, { -2.320230938e-03f, -4.798891954e-03f },
{ 1.825670945e-03f, 1.580715296e-03f }, { -1.879826188e-03f, 6.539559457e-03f }, { -1.153890509e-03f, -1.871578512e-03f },
{ -3.846446285e-03f, 1.108263247e-02f }, { 1.676079631e-02f, -1.510275411e-03f }, { 1.152072800e-03f, 4.703115672e-03f },
{ -1.258932240e-02f, 5.426196847e-03f }, { 1.211719215e-02f, -2.556819702e-03f }, { 2.875600569e-02f, -2.064039931e-03f },
{ 2.302374877e-02f, 3.829095047e-03f }, { -4.200979602e-03f, -3.086568788e-03f }, { 3.564488888e-02f, -1.318836957e-02f },
{ -1.136253029e-02f, 2.440769895e-04f }, { -2.095016278e-02f, 5.774464924e-03f }, { 1.388325356e-02f, -1.105636638e-02f },
{ 1.803878322e-02f, -9.901482612e-03f }, { -3.065022640e-03f, 8.504002355e-03f }, { -3.011808731e-03f, -5.176807288e-03f },
{ 6.512018153e-04f, 8.132720832e-04f }, { -5.101013463e-03f, -2.327305963e-03f }, { -4.732127301e-03f, -1.507790526e-03f },
{ 1.980553614e-03f, 1.223168569e-03f }, { -4.459375050e-03f, -9.043966420e-04f }, { -1.054605562e-02f, -1.585059101e-03f },
{ -9.816198610e-03f, 4.070995841e-03f }, { 1.447615679e-03f, 5.477414015e-05f }, { -6.903338246e-03f, 2.097342862e-03f },
{ 8.691789117e-04f, -1.394752297e-03f }, { -1.128911041e-03f, -3.116167383e-03f }, { -4.925142974e-03f, -1.136097708e-03f },
{ -2.611270174e-04f, -1.441219472e-03f }, { -2.003973117e-03f, 2.346458612e-03f }, { 9.383748285e-03f, 8.703176863e-03f },
{ 5.156501662e-03f, -1.970787533e-03f }, { -2.604289446e-03f, -3.790183109e-04f }, { 7.721356815e-04f, 1.972476486e-03f },
{ 3.145763883e-03f, -2.144733153e-04f }, { -4.673007876e-03f, 3.172734287e-03f }, { -3.764079185e-03f, -8.328152820e-03f },
{ -4.485322628e-03f, 5.514724180e-03f }, { -2.461444587e-03f, -1.165697630e-02f }, { 8.184628561e-03f, 1.443392131e-03f },
{ -4.455132410e-03f, 2.200886840e-03f }, { 2.179021714e-03f, -2.328114118e-03f }, { 7.237539161e-03f, -9.669236839e-03f },
{ -8.640550077e-03f, -9.054285474e-03f }, { -1.206108485e-03f, -1.235498639e-04f }, { 1.546770241e-02f, 2.729752101e-03f },
{ 1.731253974e-02f, 2.068402944e-03f }, { -2.271239832e-02f, -1.498918748e-03f }, { -1.283845399e-02f, 3.128156299e-03f },
{ 8.735341020e-03f, 7.079254836e-03f }, { 9.860594757e-03f, 1.258777920e-02f }, { 2.355418546e-04f, -3.024128091e-04f },
{ -8.113291115e-03f, -9.543613531e-03f }, { 2.948532579e-03f, 6.702618673e-03f }, { 7.945939898e-03f, -1.248920918e-03f },
{ 2.162894234e-03f, -1.145169954e-03f }, { -6.329953671e-03f, -2.390238224e-03f }, { 5.018929020e-03f, 7.005940657e-03f },
{ 5.766151589e-04f, -5.306253675e-03f }, { -2.266372554e-03f, -1.571175293e-03f }, { -3.949624021e-03f, 4.711580463e-03f },
{ -2.273411723e-03f, -1.144108464e-04f }, { 1.253100461e-03f, -3.576075193e-03f }, { 4.559647641e-04f, 7.038487820e-04f },
{ -5.256180884e-04f, 9.299787343e-04f }, { -2.848349395e-04f, -4.818271555e-04f }, { -3.020362237e-05f, 2.693361057e-05f },
{ 3.569063265e-04f, -5.284890358e-05f }, { -7.915252820e-03f, 1.448417921e-02f }, { -6.198265776e-02f, 6.116967183e-03f },
{ 1.139590219e-01f, -8.882056177e-02f }, { 1.860605031e-01f, -1.029657871e-01f }, { 8.342682570e-02f, -2.175923139e-01f },
{ -1.267465651e-01f, -1.695801318e-01f }, { 1.777947247e-01f, -9.905706346e-02f }, { -4.146566093e-01f, 1.099241525e-01f },
{ -6.887235045e-01f, -4.032902122e-01f }, { -1.824623048e-01f, 2.404407114e-01f }, { 2.246285090e-03f, -6.943063810e-03f },
{ 3.249513451e-03f, 1.820311300e-03f }, { 2.485736646e-02f, 2.043555258e-03f }, { 1.782027073e-02f, 4.338612314e-03f },
{ 1.211636467e-03f, 1.946543693e-03f }, { 4.025410116e-02f, 2.226956189e-03f }, { -6.107665226e-02f, 5.317269242e-04f },
{ 2.078279667e-02f, -1.439446933e-03f }, { -9.420170449e-03f, -1.485835761e-02f }, { 5.996773485e-03f, 7.175534964e-03f },
{ 1.865146309e-02f, -8.691115072e-04f }, { 7.165045245e-04f, -5.412854254e-03f }, { -1.429604273e-02f, 1.367502497e-03f },
{ 1.265187562e-02f, -2.807899727e-04f }, { -1.218517311e-02f, -9.408306214e-04f }, { 1.495581027e-02f, -1.238323469e-02f },
{ -2.702118829e-03f, 9.812001139e-03f }, { 5.178106483e-03f, -2.784847282e-03f }, { -6.819553673e-03f, -7.969203289e-04f },
{ 3.727761097e-03f, 4.883892834e-03f }, { -3.865442704e-03f, 5.255972501e-04f }, { 1.125461538e-03f, 7.236050908e-03f },
{ -7.945659570e-03f, 5.072486121e-03f }, { -1.109648217e-02f, 7.179707754e-03f }, { 1.727613504e-03f, 4.729861685e-04f },
{ 9.447506629e-03f, 5.599674187e-04f }, { 2.738567814e-02f, 4.963728599e-03f }, { 2.249464020e-02f, -3.670364851e-03f },
{ -3.687032033e-03f, 3.666515928e-03f }, { -4.840126261e-03f, -7.933497429e-03f }, { 8.647885174e-03f, 9.121629409e-03f },
{ 3.246227279e-02f, -3.295462811e-03f }, { 2.384427376e-02f, 7.305985782e-03f }, { 3.664306924e-02f, -1.790498942e-02f },
{ -4.988099262e-02f, 1.754104858e-03f }, { 6.852663308e-02f, 1.195446402e-01f }, { -8.833049238e-02f, 2.200989127e-01f },
{ -3.651116192e-01f, 3.371975124e-01f }, { -2.209227383e-01f, 3.000091612e-01f }, { 4.843607843e-01f, -2.104127966e-02f },
{ 1.098719388e-01f, -3.420371413e-01f }
};
static const uint8_t stream_b[2304] = {
0xa2,0x00,0x03,0x29,0x69,0x00,0x00,0x08,0x00,0x00,0x24,0x00,0x00,0x55,0x91,0xb8,
0xe1,0x61,0xe8,0x94,0xef,0xc0,0x39,0x47,0x40,0x27,0xe3,0x80,0x1e,0x35,0xa7,0x47,
0x4c,0x00,0x3a,0x06,0xf0,0x7d,0x40,0x07,0x8e,0x1c,0x10,0x04,0x67,0x40,0xa8,0x74,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x1e,0x2e,0x3d,0x14,0xd0,
0x76,0x47,0x6e,0xef,0x6e,0x51,0x01,0x0c,0x28,0x62,0xe5,0xe7,0x18,0x00,0xfc,0xff,
0xa2,0x00,0x03,0x3c,0xe4,0x00,0x00,0x18,0x00,0x00,0x09,0x00,0x01,0x9f,0x51,0xe9,
0x5c,0x5d,0xc6,0x00,0x00,0x00,0x00,0x99,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0xc0,
0x03,0xc8,0x00,0x00,0x04,0x0a,0x00,0x00,0x00,0xc0,0x01,0xc0,0x40,0x01,0x00,0x00,
0x00,0x03,0x00,0x01,0xc0,0xf0,0x00,0x00,0x00,0xc1,0x26,0x14,0x09,0x06,0xd6,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x01,0x10,0x70,0x0e,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xd0,0x90,0xb8,0x88,0x21,0x01,0x2e,0x75,0x11,0xaf,0xf7,0x0e,0x00,
0x0b,0xa6,0xa6,0x81,0x5a,0x00,0x00,0x00,0x00,0x00,0xa0,0x58,0x12,0x00,0x0d,0x13,
0xa2,0x26,0xa0,0x01,0x95,0xdb,0x80,0x00,0x0c,0x00,0x00,0x12,0x00,0x00,0xae,0xac,
0xbc,0x6f,0x43,0x13,0xa0,0x74,0x38,0x0d,0x87,0xa0,0x00,0xe8,0x00,0x00,0x1e,0x3f,
0xc3,0x40,0x00,0x56,0xc8,0x3c,0xfa,0x3e,0x3a,0x67,0x08,0x73,0x80,0x1d,0x1a,0x0f,
0x10,0x50,0x28,0xff,0x18,0xe2,0x9c,0x06,0xf8,0x00,0x01,0xe0,0x01,0xf8,0xc3,0xa3,
0x80,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xc0,0x00,0x28,0x5c,0x28,0x94,0x00,0x38,0xa8,0x01,0x31,0xd8,
0xb1,0x00,0x00,0x00,0x40,0x87,0x79,0x5c,0x55,0x1f,0x00,0x6c,0x2f,0xf8,0xf8,0xf8,
0xa2,0x00,0x03,0x33,0x3b,0x00,0x00,0x08,0x00,0x00,0x21,0x00,0x00,0xa4,0x92,0x39,
0xe2,0x5e,0x16,0xf4,0x07,0x00,0x80,0x03,0x80,0x00,0x00,0x00,0x24,0x00,0x04,0x78,
0x03,0x80,0x01,0x00,0x00,0xcc,0x00,0x38,0x00,0x03,0xd0,0x43,0x93,0x43,0x41,0x08,
0x00,0x08,0x33,0xc0,0x00,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x0c,0x00,0x5c,
0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x40,0x00,0x04,0x40,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x07,0x43,0x30,0x1d,0x11,0x00,0xd0,0xc0,0xc0,0x13,0xd0,0x00,0x30,0x13,
0x30,0x50,0xc0,0xc5,0x00,0x00,0x00,0x0c,0x13,0x04,0x00,0x0c,0x00,0x01,0x47,0x3c,
0x0c,0x01,0xc0,0x30,0x55,0xcd,0xc1,0x00,0x0c,0x40,0xc0,0x30,0x40,0x10,0x00,0x03,
0x70,0x04,0x33,0x03,0xd0,0x10,0xc7,0xc0,0xc0,0x04,0x00,0x40,0x00,0x30,0x84,0x0f,
0xbd,0xe7,0x02,0x00,0x00,0x00,0x40,0x0c,0xb4,0x48,0x99,0xa5,0x05,0x8a,0x01,0x80,
0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0xc0,0x3d,0x3f,0x00,0x40,0x2a,0x9f,0x8c,
0xa2,0x00,0x03,0x23,0x0e,0x00,0x00,0x38,0x00,0x00,0x2b,0x00,0x01,0x15,0x65,0x79,
0x65,0x55,0xe9,0xc1,0xc8,0x1b,0x20,0x07,0xbd,0xc0,0xdc,0x79,0x81,0xaa,0x97,0x00,
0x00,0x03,0xdb,0x3e,0x7a,0x1d,0x40,0x48,0x20,0x80,0x00,0x03,0xf2,0x10,0x01,0xe7,
0x9f,0x01,0xe1,0xf4,0x1a,0x0a,0x03,0xe5,0x20,0x00,0x47,0x40,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x40,0xa3,0xe5,0x7f,0xd7,0x7b,0x30,0x25,0xdf,0xc5,0x48,0xee,0x86,0x69,0x50,0xff,
0x72,0xf0,0x83,0x1e,0x90,0xd5,0xd5,0xed,0x08,0xc2,0x41,0x33,0x2f,0xf3,0x34,0x72,
0x0c,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x4f,0x43,0x03,0x00,0xfe,0x5d,0xf8,
0xa2,0x00,0x03,0x39,0x33,0x00,0x00,0x28,0x00,0x00,0x0a,0x00,0x00,0x9c,0x51,0xe7,
0xe7,0x62,0x06,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x00,0x00,0x31,0x80,0x1a,0x00,
0x00,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0xc0,0x4c,0x00,0x00,0x00,
0x01,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x38,0x40,0x90,0x08,
0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x0e,0x60,0xd8,0x08,0x00,0xc8,
0x83,0xc0,0xf6,0x00,0x03,0x14,0x7f,0x3c,0x98,0x69,0x57,0x5d,0x1b,0x00,0x3c,0xf0,
0xa2,0x00,0x03,0x24,0xf2,0x00,0x00,0x28,0x00,0x00,0x19,0x00,0x01,0x65,0x5d,0xd9,
0xa7,0x8a,0x28,0x80,0x1a,0x07,0x63,0xb0,0x06,0x80,0x00,0xf8,0x00,0x67,0xe7,0xa7,
0x3d,0x06,0x32,0x38,0x0c,0x60,0xe0,0x4c,0x1f,0x73,0x71,0xe2,0x9a,0x4f,0xc3,0x9f,
0x1b,0xce,0xd9,0xc0,0xe6,0x00,0x00,0x01,0xff,0xc7,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1d,0x41,0xc0,0x13,0x0c,0x0c,
0x1c,0x10,0x1f,0x00,0x70,0x81,0x01,0x13,0x00,0x00,0x04,0x20,0xa5,0x3e,0x0d,0x00,
0x40,0x56,0x67,0x8a,0x5c,0x00,0x00,0x00,0x00,0x00,0x70,0xca,0x12,0x00,0xcd,0x06,
0xa2,0x26,0xa0,0x01,0x9a,0x71,0x00,0x00,0x10,0x00,0x00,0x18,0x80,0x00,0x2a,0x3f,
0x0a,0x90,0xcc,0xf2,0xa0,0x00,0x00,0x00,0x84,0x02,0x60,0x9e,0x01,0xa0,0x01,0x02,
0x00,0x00,0x01,0xc1,0xc8,0x1f,0xdc,0x00,0x20,0x00,0x03,0x41,0x1a,0x80,0x80,0x03,
0x80,0x00,0x00,0x20,0x04,0x40,0x00,0x04,0x00,0x07,0xaa,0x00,0x00,0x00,0x61,0xe0,
0x00,0x00,0x66,0x00,0x00,0x78,0x60,0x80,0x00,0x00,0x00,0x08,0x81,0x80,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf4,0x03,0x03,0x01,0x10,0x04,0x80,
0x01,0x44,0x00,0x60,0x20,0xc2,0xa5,0x5d,0x1b,0x27,0x1d,0x00,0x5c,0xa9,0xf8,0xf8,
0xa2,0x00,0x03,0x2d,0xeb,0x00,0x00,0x38,0x00,0x00,0x33,0x00,0x01,0x9f,0x61,0x49,
0xa1,0x55,0x49,0x87,0x20,0x62,0xc0,0x0a,0xc0,0x00,0x44,0x93,0x00,0xf0,0xdc,0x61,
0x38,0x00,0x00,0x05,0x80,0x7a,0x89,0xc0,0x00,0x10,0x03,0x7a,0x81,0xc0,0x1d,0x60,
0x7f,0x17,0x41,0x8a,0x00,0x9e,0x3f,0x8f,0x2c,0x01,0x9a,0x6a,0x00,0x0e,0xba,0x7e,
0x00,0x0f,0xa0,0xa8,0x00,0x90,0xf9,0x00,0x01,0xb1,0xd0,0x00,0x39,0x01,0x40,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xc6,0xdd,0x72,0xc9,0x8d,0x9c,0xad,0x6d,0x34,
0x43,0x6a,0xca,0x0c,0x26,0x65,0x3b,0xe2,0xcd,0x85,0x37,0x6a,0x5a,0x3b,0x24,0x2e,
0x73,0xb6,0x4d,0xaf,0x8d,0xe6,0xa2,0x61,0x85,0xca,0x24,0x00,0x20,0x1f,0xfc,0xac,
0xb2,0x87,0x01,0xfa,0x00,0xd6,0x80,0xc7,0x6d,0x69,0x69,0xe2,0xc6,0x65,0x01,0x80,
0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x70,0x9e,0x3e,0x00,0x40,0x2a,0xaf,0x3e,
0xa2,0x00,0x03,0x36,0x97,0x00,0x00,0x10,0x00,0x00,0x0d,0x00,0x00,0xd5,0x85,0x86,
0x19,0x9e,0x05,0x00,0xa8,0x0c,0xf6,0x00,0x00,0x00,0x00,0x00,0x00,0x3a,0xd0,0x00,
0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x11,0xc3,0x40,0x71,0x00,0x0c,0x10,0x00,
0x10,0x03,0x40,0x00,0x03,0x80,0x00,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x00,
0x18,0x00,0x00,0x00,0xbc,0x00,0xc0,0x4c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,
0xf8,0xee,0xeb,0x03,0x34,0x6a,0x00,0x33,0x28,0x7a,0x58,0x99,0x1a,0x00,0xfc,0xff,
0xa2,0x00,0x03,0x24,0xe2,0x00,0x00,0x28,0x00,0x00,0x22,0x00,0x01,0x1b,0x5d,0xe6,
0x24,0x62,0x45,0x9d,0x0b,0xb8,0xe0,0xf9,0x4f,0xf4,0x3d,0x70,0xf7,0xe8,0x0f,0x4f,
0x80,0x3e,0xe0,0x33,0x8f,0x87,0xfe,0xbc,0x6b,0xe6,0x1c,0xe0,0x00,0x02,0x06,0x06,
0x7e,0x3c,0x90,0x40,0x47,0x87,0xbe,0x0e,0x8e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xcc,0x00,0x30,0x70,0xd0,0x70,0x10,
0x00,0x06,0x00,0x80,0x0f,0x84,0x46,0x00,0x27,0x06,0x74,0x08,0xd8,0x00,0x00,0x42,
0x00,0xda,0x78,0x81,0x62,0x00,0x00,0x00,0x00,0x00,0xb8,0xe4,0x12,0x00,0x1d,0x71,
0xa2,0x00,0x03,0x3a,0x4f,0x00,0x00,0x18,0x00,0x00,0x1d,0x00,0x00,0x54,0x6a,0x36,
0x59,0x7e,0x26,0x40,0x00,0x29,0xec,0x30,0x01,0x00,0x04,0x28,0x00,0x00,0x28,0x01,
0x70,0x00,0x00,0x0e,0x00,0x00,0x72,0x00,0x00,0x00,0x5b,0x64,0x0a,0x18,0xa0,0x00,
0xb3,0x74,0x00,0x2c,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x00,0x07,0xb4,0x02,0xc0,
0x00,0x00,0x03,0x84,0x00,0x00,0x00,0x0c,0xc0,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x6c,0xfa,0x9b,0x19,0x8c,0x61,0x00,
0x35,0x1e,0x00,0x00,0x00,0x00,0x70,0x66,0x89,0x66,0xe7,0x1c,0x00,0xec,0xc2,0xf8
};
/* every 97th sample, left then right */
static const float ref_b[127][2] = {
{ 9.242755361e-17f, 1.773212686e-17f }, { -1.376676937e-05f, 5.907738796e-06f }, { -5.874811177e-05f, 1.788824011e-04f },
{ -1.018570038e-03f, 2.464378485e-04f }, { -2.119569399e-04f, 7.377937436e-04f }, { 1.533148461e-03f, 9.435778484e-04f },
{ 1.381888869e-03f, 1.630387618e-03f }, { -8.613474667e-03f, -6.845163181e-03f }, { -2.718295436e-03f, -8.763763122e-03f },
{ 7.736574858e-03f, -2.141328296e-03f }, { 5.018206779e-03f, -2.315925667e-03f }, { 7.489276584e-03f, -1.907932223e-03f },
{ 3.134268802e-03f, -1.610284904e-03f }, { -3.251069225e-03f, -9.966419078e-03f }, { 1.527063362e-02f, 2.392249880e-03f },
{ 9.232130833e-03f, -4.118668585e-06f }, { 2.157462150e-04f, -4.446413834e-03f }, { 7.393121254e-03f, 3.141878173e-03f },
{ -2.523394069e-03f, 2.551409125e-04f }, { -1.967095770e-03f, -7.605865598e-03f }, { 2.726608654e-03f, 4.839247558e-03f },
{ 9.896017145e-04f, 3.117912682e-03f }, { -1.379047288e-03f, -8.059814572e-03f }, { -1.710425131e-03f, 6.043954752e-03f },
{ -4.109190311e-03f, 1.150204800e-02f }, { 5.927567370e-03f, -1.051148307e-02f }, { -3.755969694e-03f, -1.236700173e-02f },
{ 5.969046033e-04f, -1.793625206e-02f }, { -4.289786797e-03f, 7.779521868e-03f }, { 5.922518205e-03f, 1.685886644e-02f },
{ -1.450977870e-03f, 1.807238720e-02f }, { 2.093485091e-03f, -3.580174409e-03f }, { -1.901602489e-03f, -5.925749429e-03f },
{ -1.028684084e-03f, 1.101716235e-02f }, { 6.783198100e-03f, 5.522613879e-03f }, { 4.541845061e-03f, 7.188364863e-03f },
{ -7.084840443e-03f, 8.068239549e-04f }, { 1.375457650e-04f, 1.930533722e-02f }, { 1.656097919e-02f, 1.760038361e-02f },
{ -1.186118461e-03f, 3.760735318e-02f }, { -5.043789279e-03f, 1.314742863e-02f }, { -3.093298292e-04f, 3.411643847e-04f },
{ 1.251654699e-02f, -1.258237846e-02f }, { 1.087525114e-02f, -2.453862503e-02f }, { 7.555384189e-02f, -2.235571155e-03f },
{ 5.234836042e-02f, 4.892709665e-03f }, { 3.225525469e-02f, 5.978997797e-03f }, { 3.580965335e-03f, 9.903170168e-03f },
{ 5.783449113e-02f, -9.272078983e-03f }, { -2.673234791e-02f, -3.140788060e-03f }, { 9.830247611e-03f, 1.172329299e-02f },
{ 7.472440135e-03f, 9.373517707e-03f }, { -8.061029017e-03f, -4.368110094e-03f }, { -1.345834043e-02f, -1.348030660e-02f },
{ 1.986056566e-02f, 7.570201997e-03f }, { 3.439211752e-03f, 4.588234238e-03f }, { -2.303057723e-02f, -8.068498224e-03f },
{ 3.217371996e-04f, 1.693903119e-03f }, { 2.076347545e-02f, 6.854519714e-03f }, { 4.996277392e-03f, 1.656256150e-03f },
{ -9.310330264e-03f, -1.143144537e-02f }, { -3.636383219e-03f, -1.356176334e-03f }, { 8.796998882e-04f, 3.965437878e-03f },
{ 6.642710883e-03f, 5.764317233e-03f }, { 9.057056159e-04f, -1.389055629e-03f }, { -9.527881630e-04f, -1.039899536e-03f },
{ -5.453730118e-04f, -7.614314673e-04f }, { -1.817151555e-03f, -7.841610350e-03f }, { 1.814443385e-03f, -2.783074416e-02f },
{ -7.000409532e-03f, 8.080396801e-03f }, { 7.318077143e-03f, 1.025662571e-02f }, { -9.270641953e-03f, -5.419919267e-02f },
{ -4.289797507e-03f, 4.784114659e-03f }, { -1.283136290e-02f, 2.585908212e-02f }, { -2.063469775e-02f, 2.611474693e-02f },
{ -1.088263746e-02f, -2.781113377e-03f }, { -4.381132126e-02f, 2.648726571e-03f }, { -1.627543941e-02f, 1.495551667e-03f },
{ -1.758247986e-02f, 3.056185134e-02f }, { -2.161317505e-02f, 3.084198339e-03f }, { -3.460384207e-03f, 5.233756732e-03f },
{ -4.000281543e-02f, 3.118669428e-02f }, { -3.414784744e-02f, 1.190567622e-03f }, { -1.392255258e-02f, 5.745146424e-03f },
{ 4.076865036e-03f, 8.290714584e-03f }, { 2.667550230e-03f, -2.540340833e-02f }, { 2.732063085e-02f, 1.718526590e-03f },
{ -6.838950329e-03f, -9.659450734e-04f }, { -3.738116100e-02f, -1.083254628e-02f }, { 4.238779470e-02f, 1.795529155e-03f },
{ 5.989202112e-02f, 2.687947825e-03f }, { 3.961244971e-02f, -1.531511266e-02f }, { 8.675146848e-02f, -1.046039443e-02f },
{ 3.647199646e-02f, -7.589594927e-03f }, { 6.936357357e-03f, -2.834746847e-03f }, { 4.016721994e-02f, -4.069634713e-03f },
{ 1.770015806e-03f, -3.062386252e-02f }, { 5.142065929e-04f, 3.951912280e-03f }, { 6.848720368e-03f, -1.778870448e-02f },
{ 2.332988754e-02f, -2.948206849e-02f }, { -2.872671001e-03f, 4.000978172e-02f }, { -1.185696805e-03f, 4.486579075e-02f },
{ 8.405883797e-03f, 6.196010858e-02f }, { 5.209826049e-04f, -1.904802211e-02f }, { 4.224623553e-03f, 1.020930591e-03f },
{ -3.941879142e-03f, -8.879946545e-03f }, { -5.200563464e-03f, 1.905284473e-03f }, { 1.238461491e-02f, 1.621735469e-02f },
{ 1.755487546e-02f, 1.866262965e-02f }, { 7.159634493e-03f, 7.472440600e-03f }, { -9.791470133e-03f, -9.339432232e-03f },
{ -3.184349742e-03f, 4.346971764e-05f }, { 7.015237119e-03f, 1.872387156e-02f }, { -1.548395865e-02f, -1.042172778e-03f },
{ 1.337395981e-02f, 1.080276817e-02f }, { 1.752130105e-03f, 3.874791786e-03f }, { -7.943656296e-03f, -9.540103376e-03f },
{ 6.131154019e-03f, -5.150374956e-03f }, { -9.605278261e-03f, 2.796929143e-02f }, { 1.565933228e-02f, 5.550820846e-03f },
{ -6.927136797e-03f, 3.162872745e-03f }, { 1.543233451e-02f, -2.282161964e-03f }, { 7.871425711e-03f, 1.122876909e-02f },
{ 2.274056152e-02f, -5.603998434e-03f }, { -6.363321561e-03f, -1.313319081e-03f }, { 1.146048587e-02f, -2.974855248e-03f },
{ 1.110625733e-02f, -8.095641620e-03f }
};
+37
View File
@@ -0,0 +1,37 @@
include ../../sample_objdir.mk
TARGET := rpsmf_test
LIBRETRO_COMM_DIR := ../../..
SOURCES := \
rpsmf_test.c \
$(LIBRETRO_COMM_DIR)/formats/psmf/rpsmf.c \
$(LIBRETRO_COMM_DIR)/formats/mpeg1/rmpeg1_ps.c
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
CFLAGS += -Wall -pedantic -std=c89 -g -O1 -I$(LIBRETRO_COMM_DIR)/include
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: clean
+285
View File
@@ -0,0 +1,285 @@
/* rpsmf and the MPEG-2 side of rmpeg1_ps against program streams built
* here: a PSMF header, MPEG-2 packs and PES headers, H.264 video with
* PTS and DTS, and ATRAC3plus frames running across private stream 1
* packets behind the packets' own header bytes - fed whole, a byte at a
* time and in odd chunks, with a broken frame in between. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <formats/rpsmf.h>
#include <formats/rmpeg1_ps.h>
static unsigned failures;
#define CHECK(c, msg) do { if (!(c)) { failures++; \
printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); } } while (0)
static uint8_t ps[1 << 16];
static size_t ps_len;
static void put(const void *p, size_t n)
{
if (n)
memcpy(ps + ps_len, p, n);
ps_len += n;
}
static void put_ts(unsigned prefix, uint64_t ts)
{
uint8_t b[5];
b[0] = (uint8_t)((prefix << 4) | (((ts >> 30) & 7) << 1) | 1);
b[1] = (uint8_t)(ts >> 22);
b[2] = (uint8_t)((((ts >> 15) & 0x7F) << 1) | 1);
b[3] = (uint8_t)(ts >> 7);
b[4] = (uint8_t)(((ts & 0x7F) << 1) | 1);
put(b, 5);
}
/* An MPEG-2 pack header, SCR @scr, two stuffing bytes */
static void put_pack2(uint64_t scr)
{
uint8_t b[16];
uint32_t mux = 0x1234;
b[0] = 0; b[1] = 0; b[2] = 1; b[3] = 0xBA;
b[4] = (uint8_t)(0x44 | (((scr >> 30) & 7) << 3) | ((scr >> 28) & 3));
b[5] = (uint8_t)(scr >> 20);
b[6] = (uint8_t)(((scr >> 15) & 0x1F) << 3 | 4 | ((scr >> 13) & 3));
b[7] = (uint8_t)(scr >> 5);
b[8] = (uint8_t)(((scr & 0x1F) << 3) | 4);
b[9] = 1;
b[10] = (uint8_t)(mux >> 14);
b[11] = (uint8_t)(mux >> 6);
b[12] = (uint8_t)(((mux & 0x3F) << 2) | 3);
b[13] = 0xF8 | 2;
b[14] = 0xFF;
b[15] = 0xFF;
put(b, 16);
}
/* An MPEG-2 PES of @id: PTS (and DTS) when given, @lead bytes before
* @payload (a private stream's substream byte and header) */
static void put_pes2(uint8_t id, uint64_t pts, uint64_t dts,
const uint8_t *lead, size_t lead_len,
const uint8_t *payload, size_t len)
{
uint8_t b[9];
size_t hlen = pts == RPSMF_NO_PTS ? 0 : dts == RPSMF_NO_PTS ? 5 : 10;
size_t total = 3 + hlen + lead_len + len;
b[0] = 0; b[1] = 0; b[2] = 1; b[3] = id;
b[4] = (uint8_t)(total >> 8);
b[5] = (uint8_t)total;
b[6] = 0x81;
b[7] = (uint8_t)(hlen == 10 ? 0xC0 : hlen ? 0x80 : 0);
b[8] = (uint8_t)hlen;
put(b, 9);
if (hlen)
put_ts(hlen == 10 ? 3 : 2, pts);
if (hlen == 10)
put_ts(1, dts);
put(lead, lead_len);
put(payload, len);
}
/* Two ATRAC3plus frames back to back, headers and all, and a third */
static uint8_t atrac[3][8 + 376];
static uint8_t audio[3 * (8 + 376)];
static void make_frames(void)
{
unsigned f, i;
for (f = 0; f < 3; f++)
{
atrac[f][0] = 0x0F; atrac[f][1] = 0xD0;
atrac[f][2] = 0x28; atrac[f][3] = 0x2E;
atrac[f][4] = atrac[f][5] = atrac[f][6] = atrac[f][7] = 0;
for (i = 0; i < 376; i++)
atrac[f][8 + i] = (uint8_t)(i * 7 + f * 31 + 1);
memcpy(audio + f * (8 + 376), atrac[f], 8 + 376);
}
}
static const uint8_t video[] = {
0, 0, 0, 1, 0x09, 0xF0, 0, 0, 0, 1, 0x67, 0x4D, 0x40, 0x1E };
/* A movie: header, then video, then the three frames across three
* packets of substream 0 with @skip header bytes each, the second
* frame split mid-way, and, when @broken, junk where the third frame's
* header should be continued */
static size_t build(uint8_t *file, unsigned skip, bool broken)
{
uint8_t lead[8];
size_t a = 8 + 376;
unsigned i;
ps_len = 0;
memset(lead, 0xAA, sizeof(lead));
lead[0] = 0x00;
put_pack2(1000);
put_pes2(0xE0, 9000, 6000, NULL, 0, video, sizeof(video));
/* frame 0 whole, frame 1's first 100 bytes */
put_pes2(0xBD, 9100, RPSMF_NO_PTS, lead, 1 + skip, audio, a + 100);
put_pack2(2000);
/* the rest of frame 1, and frame 2's header and 50 bytes */
put_pes2(0xBD, 9500, RPSMF_NO_PTS, lead, 1 + skip, audio + a + 100,
a - 100 + 8 + 50);
if (broken)
ps[ps_len - 50 - 8] ^= 0xFF; /* frame 2's header broken... */
/* ...and the rest of it */
put_pes2(0xBD, RPSMF_NO_PTS, RPSMF_NO_PTS, lead, 1 + skip,
audio + 2 * a + 8 + 50, a - 8 - 50);
/* a fresh frame after, which a broken stream recovers on */
put_pes2(0xBD, 9900, RPSMF_NO_PTS, lead, 1 + skip, atrac[0], a);
ps[ps_len++] = 0; ps[ps_len++] = 0; ps[ps_len++] = 1; ps[ps_len++] = 0xB9;
memset(file, 0, 0x800);
memcpy(file, "PSMF0015", 8);
file[0x0A] = 0x08; /* data offset 0x800 */
file[0x0C] = (uint8_t)(ps_len >> 24);
file[0x0D] = (uint8_t)(ps_len >> 16);
file[0x0E] = (uint8_t)(ps_len >> 8);
file[0x0F] = (uint8_t)ps_len;
file[0x57] = 0x01; file[0x58] = 0x5F; file[0x59] = 0x90; /* 90000 */
file[0x5D] = 0x01; /* 65536 */
file[0x62] = 0x12; file[0x63] = 0x34;
for (i = 0; i < ps_len; i++)
file[0x800 + i] = ps[i];
return 0x800 + ps_len;
}
typedef struct
{
unsigned video, frames, other, borrowed;
uint64_t video_pts, video_dts;
uint64_t pts[8];
bool ok[8];
} seen_t;
static const uint8_t *in_lo, *in_hi;
static void drain(rpsmf_t *d, seen_t *s)
{
rpsmf_packet_t p;
while (rpsmf_next(d, &p))
{
if (p.kind == RPSMF_VIDEO)
{
CHECK(p.size == sizeof(video) && !memcmp(p.data, video, p.size),
"video payload");
s->video_pts = p.pts;
s->video_dts = p.dts;
s->video++;
}
else if (p.kind == RPSMF_ATRAC3P && s->frames < 8)
{
unsigned f = s->frames % 3;
/* the last frame is atrac[0] again */
if (s->frames >= 2)
f = (p.pts == 9900) ? 0 : 2;
s->ok[s->frames] = p.size == 376 && p.params[0] == 0x28
&& p.params[1] == 0x2E && !memcmp(p.data, atrac[f] + 8, 376);
s->pts[s->frames] = p.pts;
if (p.data >= in_lo && p.data < in_hi)
s->borrowed++;
s->frames++;
}
else
s->other++;
}
}
/* @borrow: the input parsed where it lies, rather than written in */
static void run(const uint8_t *file, size_t len, size_t chunk,
bool borrow, seen_t *s)
{
rpsmf_info_t info;
rpsmf_t *d = rpsmf_init();
size_t off;
in_lo = file;
in_hi = file + len;
memset(s, 0, sizeof(*s));
CHECK(rpsmf_parse_header(file, len, &info), "header");
CHECK(info.version == 15 && info.data_offset == 0x800
&& info.tick_freq == 90000 && info.duration == 65536
&& info.mux_rate == 0x1234, "header fields");
for (off = info.data_offset; off < len; )
{
size_t n = len - off < chunk ? len - off : chunk;
size_t w = borrow ? rpsmf_borrow(d, file + off, n)
: rpsmf_write(d, file + off, n);
off += w;
drain(d, s);
}
drain(d, s);
rpsmf_free(d);
}
static uint8_t file[0x800 + (1 << 16)];
int main(void)
{
static const size_t chunks[] = { (size_t)-1, 1, 7, 2048 };
unsigned skip, c, b;
make_frames();
for (b = 0; b < 2; b++)
for (skip = 3; skip <= 5; skip += 2)
for (c = 0; c < sizeof(chunks) / sizeof(chunks[0]); c++)
{
seen_t s;
size_t len = build(file, skip, false);
run(file, len, chunks[c], b != 0, &s);
CHECK(s.video == 1 && s.video_pts == 9000 && s.video_dts == 6000,
"video PTS and DTS");
CHECK(s.frames == 4, "four ATRAC3plus frames");
CHECK(s.ok[0] && s.ok[1] && s.ok[2] && s.ok[3], "frames whole");
/* a packet's PTS is the first frame to start in it */
CHECK(s.pts[0] == 9100 && s.pts[1] == RPSMF_NO_PTS
&& s.pts[2] == 9500 && s.pts[3] == 9900, "frame PTS");
CHECK(!s.other, "nothing else");
/* fed in pieces as large as a packet, at least the first frame
* lies whole where it was handed over */
if (b && chunks[c] >= 2048)
CHECK(s.borrowed >= 1, "frames whole in a packet not copied");
len = build(file, skip, true);
run(file, len, chunks[c], b != 0, &s);
/* the broken one goes; the stream comes back on the next */
CHECK(s.frames == 3 && s.ok[0] && s.ok[1] && s.ok[2]
&& s.pts[2] == 9900, "recovers after a broken frame");
}
/* MPEG-1 still reads as it did */
{
static const uint8_t mpeg1[] = {
0, 0, 1, 0xBA, 0x21, 0x00, 0x01, 0x00, 0x01, 0x80, 0x00, 0x01,
0, 0, 1, 0xE0, 0, 9, 0x21, 0x00, 0x01, 0x00, 0x01, 0xAB, 0xCD,
0xEF, 0x12,
0, 0, 1, 0xB9 };
rmpeg1_ps_packet_t p;
rmpeg1_ps_t *m = rmpeg1_ps_init(0);
rmpeg1_ps_write(m, mpeg1, sizeof(mpeg1));
CHECK(rmpeg1_ps_next(m, &p) && p.type == RMPEG1_PS_VIDEO
&& p.size == 4 && p.pts == 0 && p.data[0] == 0xAB,
"MPEG-1 PES");
rmpeg1_ps_free(m);
}
/* Not a PSMF */
{
rpsmf_info_t info;
memcpy(file, "PSMX0015", 8);
CHECK(!rpsmf_parse_header(file, 0x800, &info), "bad magic");
memcpy(file, "PSMF00x5", 8);
CHECK(!rpsmf_parse_header(file, 0x800, &info), "bad version");
CHECK(!rpsmf_parse_header(file, 10, &info), "short");
}
if (failures)
{
printf("rpsmf_test: %u failure(s)\n", failures);
return 1;
}
printf("rpsmf_test: all lanes pass\n");
return 0;
}
@@ -404,12 +404,13 @@ static int run_frame_span_checks(void)
for (i = 0; i < sizeof(input); i++) input[i] = (uint8_t)(i * 13 + offset);
memset(next, 0xa5, sizeof(next));
if (retro_spsc_write_frames(&q, input, count, width) != count) goto fail;
/* A nonempty cache can expose less than the published head. */
/* A nonempty but stale cache: read_begin re-reads the
* published head rather than expose less than it. */
if (stale) q.cached_head = start + width;
tail = retro_atomic_load_relaxed_size(&q.tail);
available = retro_spsc_read_begin(&q, &span);
physical = 128 - offset;
cached = stale ? width : count * width;
cached = count * width;
if (available != (physical < cached ? physical : cached)) goto fail;
if (memcmp(span, input, available)) goto fail;
if (retro_spsc_write_frames(&q, next, 1, width)) goto fail;
@@ -441,12 +442,224 @@ fail:
retro_spsc_free(&q);
return 1;
}
printf("[pass] %u frame span cases: ownership, fallback, stale cache and cursor wrap\n", cases);
printf("[pass] %u frame span cases: ownership, fallback, stale cache refreshed and cursor wrap\n", cases);
return 0;
}
/* ---- mirrored rings ---------------------------------------------- */
/* Single-threaded: the mirrored ring reads like a plain one - capacity,
* room and fill - and a reservation or a read that crosses the
* buffer's end is one span, landing where the plain ring would have
* split it. Run on a ring larger than a page and on one smaller, whose
* buffer is a page but whose fill stops at the capacity asked for. */
static int run_mirror_case(size_t min_capacity)
{
retro_spsc_t q;
uint8_t *buf;
void *wp;
const void *rp;
size_t cap, size, n, i;
if (!retro_spsc_init_mirrored(&q, min_capacity))
{
fprintf(stderr, "FAIL: mirror %u: init\n", (unsigned)min_capacity);
return 1;
}
cap = q.capacity;
size = q.mask + 1;
if (!q.mirror)
{
printf("[skip] mirror %u: this platform maps no mirror, plain ring\n",
(unsigned)min_capacity);
retro_spsc_free(&q);
return 0;
}
if ( cap != min_capacity || size < cap || (size & (size - 1))
|| retro_spsc_write_avail(&q) != cap || retro_spsc_read_avail(&q))
{
fprintf(stderr, "FAIL: mirror %u: capacity %u in a %u-byte buffer\n",
(unsigned)min_capacity, (unsigned)cap, (unsigned)size);
return 1;
}
if (!(buf = (uint8_t*)malloc(cap + 512)))
return 1;
/* Fill stops at the capacity, not the buffer. */
memset(buf, 0x11, cap + 512);
if (retro_spsc_write(&q, buf, cap + 512) != cap
|| retro_spsc_write_avail(&q) != 0
|| retro_spsc_read(&q, buf, cap + 512) != cap)
{
fprintf(stderr, "FAIL: mirror %u: fill past the capacity\n",
(unsigned)min_capacity);
return 1;
}
/* Walk the cursors to 24 bytes short of the buffer's end. */
n = (size - 24 + size - (cap & (size - 1))) & (size - 1);
while (n)
{
size_t k = n > cap ? cap : n;
retro_spsc_write(&q, buf, k);
retro_spsc_read(&q, buf, k);
n -= k;
}
/* One reservation of the whole capacity, across the end. */
if (retro_spsc_write_begin(&q, &wp) != cap)
{
fprintf(stderr, "FAIL: mirror %u: reservation cut at the wrap\n",
(unsigned)min_capacity);
return 1;
}
for (i = 0; i < cap; i++)
((uint8_t*)wp)[i] = (uint8_t)(i * 7 + 3);
retro_spsc_write_end(&q, cap);
/* What went past the end is at the buffer's start. */
if (q.buffer[0] != (uint8_t)(24 * 7 + 3))
{
fprintf(stderr, "FAIL: mirror %u: the mirror is not the start\n",
(unsigned)min_capacity);
return 1;
}
if (retro_spsc_read_begin(&q, &rp) != cap)
{
fprintf(stderr, "FAIL: mirror %u: read cut at the wrap\n",
(unsigned)min_capacity);
return 1;
}
for (i = 0; i < cap; i++)
if (((const uint8_t*)rp)[i] != (uint8_t)(i * 7 + 3))
{
fprintf(stderr, "FAIL: mirror %u: byte %u\n",
(unsigned)min_capacity, (unsigned)i);
return 1;
}
retro_spsc_read_end(&q, cap);
/* The copying paths agree across the end. */
memset(buf, 0x22, 64);
n = retro_spsc_write(&q, buf, 64);
memset(buf, 0, 64);
if (n != 64 || retro_spsc_read(&q, buf, 64) != 64 || buf[0] != 0x22
|| buf[63] != 0x22)
{
fprintf(stderr, "FAIL: mirror %u: copy across the end\n",
(unsigned)min_capacity);
return 1;
}
free(buf);
retro_spsc_free(&q);
if (q.buffer || q.mirror)
{
fprintf(stderr, "FAIL: mirror %u: free left state\n",
(unsigned)min_capacity);
return 1;
}
printf("[pass] mirror %u: capacity %u in a %u-byte buffer, spans cross the end whole\n",
(unsigned)min_capacity, (unsigned)cap, (unsigned)size);
return 0;
}
/* Two threads, spans only: the producer reserves a varying amount and
* fills it with a running byte count, the consumer drains spans and
* checks the count. On a mirrored ring no span is shorter than the
* free space or the fill it reports. */
#define MIRROR_BYTES (64u << 20)
typedef struct
{
retro_spsc_t q;
unsigned long mismatches, short_writes, short_reads;
} mirror_state_t;
static void mirror_producer(void *arg)
{
mirror_state_t *s = (mirror_state_t*)arg;
size_t sent = 0, want = 1;
uint8_t v = 0;
while (sent < MIRROR_BYTES)
{
void *p;
size_t free_now = retro_spsc_write_avail(&s->q);
size_t span = retro_spsc_write_begin(&s->q, &p);
size_t i, n;
if (s->q.mirror && span < free_now)
s->short_writes++;
n = span < want ? span : want;
if (n > MIRROR_BYTES - sent)
n = MIRROR_BYTES - sent;
if (!n)
{
retro_spsc_write_end(&s->q, 0);
sthread_yield();
continue;
}
for (i = 0; i < n; i++)
((uint8_t*)p)[i] = v++;
retro_spsc_write_end(&s->q, n);
sent += n;
want = want * 5 % 997 + 1;
}
}
static void mirror_consumer(void *arg)
{
mirror_state_t *s = (mirror_state_t*)arg;
size_t got = 0;
uint8_t v = 0;
while (got < MIRROR_BYTES)
{
const void *p;
size_t fill = retro_spsc_read_avail(&s->q);
size_t span = retro_spsc_read_begin(&s->q, &p);
size_t i;
if (s->q.mirror && span < fill)
s->short_reads++;
if (!span)
{
sthread_yield();
continue;
}
for (i = 0; i < span; i++)
if (((const uint8_t*)p)[i] != v++)
s->mismatches++;
retro_spsc_read_end(&s->q, span);
got += span;
}
}
static int run_mirror_stress(size_t min_capacity)
{
mirror_state_t s;
sthread_t *prod, *cons;
memset(&s, 0, sizeof(s));
if (!retro_spsc_init_mirrored(&s.q, min_capacity))
return 1;
prod = sthread_create(mirror_producer, &s);
cons = sthread_create(mirror_consumer, &s);
if (!prod || !cons)
{
fprintf(stderr, "FAIL: mirror stress: threads\n");
return 1;
}
sthread_join(prod);
sthread_join(cons);
if (s.mismatches || s.short_writes || s.short_reads)
{
fprintf(stderr, "FAIL: mirror stress %u: %lu mismatched, %lu short reservations, %lu short reads\n",
(unsigned)min_capacity, s.mismatches, s.short_writes, s.short_reads);
retro_spsc_free(&s.q);
return 1;
}
printf("[pass] mirror stress: %u MiB through a %s %u-byte ring in spans, 0 mismatches%s\n",
MIRROR_BYTES >> 20, s.q.mirror ? "mirrored" : "plain",
(unsigned)s.q.capacity, s.q.mirror ? ", no span cut short" : "");
retro_spsc_free(&s.q);
return 0;
}
int main(void)
{
if ( run_mirror_case(1024) != 0 || run_mirror_case(65536) != 0
|| run_mirror_stress(1024) != 0 || run_mirror_stress(4096) != 0)
return 1;
if (run_frame_checks() != 0 || run_frame_span_checks() != 0
|| run_frame_stress(0) != 0 || run_frame_stress(1) != 0) return 1;
if (run_property_checks() != 0)