Initial import

This commit is contained in:
Justine Tunney 2020-06-15 07:18:57 -07:00
commit c91b3c5006
14915 changed files with 590219 additions and 0 deletions

60
dsp/bmp/bmp.mk Normal file
View file

@ -0,0 +1,60 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += DSP_BMP
DSP_BMP_ARTIFACTS += DSP_BMP_A
DSP_BMP = $(DSP_BMP_A_DEPS) $(DSP_BMP_A)
DSP_BMP_A = o/$(MODE)/dsp/bmp/bmp.a
DSP_BMP_A_FILES := $(wildcard dsp/bmp/*)
DSP_BMP_A_HDRS = $(filter %.h,$(DSP_BMP_A_FILES))
DSP_BMP_A_SRCS_S = $(filter %.S,$(DSP_BMP_A_FILES))
DSP_BMP_A_SRCS_C = $(filter %.c,$(DSP_BMP_A_FILES))
DSP_BMP_A_SRCS = \
$(DSP_BMP_A_SRCS_S) \
$(DSP_BMP_A_SRCS_C)
DSP_BMP_A_OBJS = \
$(DSP_BMP_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(DSP_BMP_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(DSP_BMP_A_SRCS_C:%.c=o/$(MODE)/%.o)
DSP_BMP_A_CHECKS = \
$(DSP_BMP_A).pkg \
$(DSP_BMP_A_HDRS:%=o/$(MODE)/%.ok)
DSP_BMP_A_DIRECTDEPS = \
LIBC_NEXGEN32E \
LIBC_TINYMATH \
LIBC_STUBS
DSP_BMP_A_DEPS := \
$(call uniq,$(foreach x,$(DSP_BMP_A_DIRECTDEPS),$($(x))))
$(DSP_BMP_A): dsp/bmp/ \
$(DSP_BMP_A).pkg \
$(DSP_BMP_A_OBJS)
$(DSP_BMP_A).pkg: \
$(DSP_BMP_A_OBJS) \
$(foreach x,$(DSP_BMP_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/dsp/bmp/float2short.o \
o/$(MODE)/dsp/bmp/scalevolume.o: \
OVERRIDE_CFLAGS += \
$(MATHEMATICAL)
o/tiny/dsp/bmp/scalevolume.o: \
OVERRIDE_CFLAGS += \
-Os
DSP_BMP_LIBS = $(foreach x,$(DSP_BMP_ARTIFACTS),$($(x)))
DSP_BMP_SRCS = $(foreach x,$(DSP_BMP_ARTIFACTS),$($(x)_SRCS))
DSP_BMP_HDRS = $(foreach x,$(DSP_BMP_ARTIFACTS),$($(x)_HDRS))
DSP_BMP_CHECKS = $(foreach x,$(DSP_BMP_ARTIFACTS),$($(x)_CHECKS))
DSP_BMP_OBJS = $(foreach x,$(DSP_BMP_ARTIFACTS),$($(x)_OBJS))
$(DSP_BMP_OBJS): $(BUILD_FILES) dsp/bmp/bmp.mk
.PHONY: o/$(MODE)/dsp/bmp
o/$(MODE)/dsp/bmp: $(DSP_BMP_CHECKS)

36
dsp/core/byte2double.c Normal file
View file

@ -0,0 +1,36 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#include "libc/mem/mem.h"
void *byte2double(long n, const void *p, double weight, double bias) {
long i;
double f, *dst;
unsigned char *src;
if ((dst = valloc(n * sizeof(double)))) {
for (src = p, i = 0; i < n; ++i) {
f = src[i];
f -= bias;
f *= 1 / weight;
dst[i] = f;
}
}
return dst;
}

22
dsp/core/c11.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef COSMOPOLITAN_DSP_CORE_C11_H_
#define COSMOPOLITAN_DSP_CORE_C11_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* Fixed-point 8-bit rounded mean kernel.
*
* @define (a + b) / 2
*/
static inline pureconst artificial unsigned char C11(unsigned char al,
unsigned char bl) {
short ax;
ax = al;
ax += bl;
ax += 1;
ax /= 2;
al = ax;
return al;
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_C11_H_ */

21
dsp/core/c121.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef COSMOPOLITAN_DSP_CORE_C121_H_
#define COSMOPOLITAN_DSP_CORE_C121_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
forceinline pureconst artificial unsigned char C121(unsigned char al,
unsigned char bl,
unsigned char cl) {
unsigned short ax, bx;
ax = al;
ax += bl;
ax += bl;
ax += cl;
ax += 2;
ax >>= 2;
return ax;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_C121_H_ */

19
dsp/core/c121s.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef COSMOPOLITAN_DSP_CORE_C121S_H_
#define COSMOPOLITAN_DSP_CORE_C121S_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
forceinline pureconst artificial signed char C121S(signed char al,
signed char bl,
signed char cl) {
short ax, bx;
ax = al;
ax += bl;
ax += bl;
ax += cl;
ax += 2;
ax >>= 2;
return ax;
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_C121S_H_ */

30
dsp/core/c1331.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef COSMOPOLITAN_DSP_CORE_C1331_H_
#define COSMOPOLITAN_DSP_CORE_C1331_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* Byte sized kernel for resampling memory in half.
*
* @define (1*𝑎 + 3*𝑏 + 3*𝑐 + 1*𝑑) / (1 + 3 + 3 + 1)
* @see C161() afterward for superior sin(𝑥)/𝑥
* @limit [0,255] [0..2,044] [0..255]
*/
forceinline pureconst artificial unsigned char C1331(unsigned char al,
unsigned char bl,
unsigned char cl,
unsigned char dl) {
short ax, bx;
bx = bl;
bx += cl;
bx *= 3;
ax = al;
ax += dl;
ax += bx;
ax += 4;
ax >>= 3;
al = ax;
return al;
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_C1331_H_ */

28
dsp/core/c1331s.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef COSMOPOLITAN_DSP_CORE_C1331S_H_
#define COSMOPOLITAN_DSP_CORE_C1331S_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* Byte sized kernel for resampling difference samples in half.
*
* @define (1*(a-128)+3*(a-128)+3*(a-128)+1*(a-128))/(1+3+3+1)+128
* @see C1331(), Y420CbCr2RgbScale()
*/
forceinline pureconst artificial signed char C1331S(signed char al,
signed char bl,
signed char cl,
signed char dl) {
short ax, bx;
bx = bl;
bx += cl;
bx *= 3;
ax = al;
ax += dl;
ax += bx;
ax += 4;
ax >>= 3;
return ax;
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_C1331S_H_ */

33
dsp/core/c161.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef COSMOPOLITAN_DSP_CORE_C161_H_
#define COSMOPOLITAN_DSP_CORE_C161_H_
#include "libc/macros.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#define EXTRA_SHARP 2
/**
* Byte sized kernel for restoring sharpness of resampled memory.
*
* @define CLAMP[(-1*𝑎 + 6*𝑏 + -1*𝑐) / (-1 + 6 + -1)]
* @limit [0..255] [-510..1,532] [-127..383] [0..255]
* @see C1331()
*/
forceinline pureconst artificial unsigned char C161(unsigned char al,
unsigned char bl,
unsigned char cl) {
short ax, bx, cx;
ax = al;
bx = bl;
cx = cl;
ax *= -1;
bx *= +6;
cx *= -1;
ax += bx;
ax += cx;
ax += 2;
ax >>= 2;
return MIN(255, MAX(0, ax));
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_C161_H_ */

26
dsp/core/c161s.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef COSMOPOLITAN_DSP_CORE_C161S_H_
#define COSMOPOLITAN_DSP_CORE_C161S_H_
#include "dsp/core/c161.h"
#include "libc/macros.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
forceinline pureconst artificial signed char C161S(signed char al,
signed char bl,
signed char cl) {
short ax, bx, cx;
ax = al;
bx = bl;
cx = cl;
ax *= -1 * EXTRA_SHARP;
bx *= 6 * EXTRA_SHARP;
cx *= -1 * EXTRA_SHARP;
ax += bx;
ax += cx;
ax += 2 * EXTRA_SHARP;
ax /= 4 * EXTRA_SHARP;
al = MIN(112, MAX(-112, ax));
return al;
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_C161S_H_ */

30
dsp/core/c331.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef COSMOPOLITAN_DSP_CORE_C331_H_
#define COSMOPOLITAN_DSP_CORE_C331_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* Fixed-point 8-bit magic edge resampling kernel.
*
* @define (3*a + 3*b + 1*c) / 7
* @see C1331()
*/
static inline pureconst artificial unsigned char C331(unsigned char al,
unsigned char bl,
unsigned char cl) {
unsigned eax, ebx, ecx;
eax = al;
ebx = bl;
ecx = cl;
eax += ebx;
eax *= 3 * 2350;
ecx *= 1 * 2350;
eax += ecx;
eax >>= 14;
al = eax;
return al;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_C331_H_ */

37
dsp/core/core.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef COSMOPOLITAN_DSP_CORE_CORE_H_
#define COSMOPOLITAN_DSP_CORE_CORE_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* @fileoverview Cosmopolitan Digital Signal Processing.
*/
int mulaw(int);
void *double2byte(long, const void *, double, double) vallocesque;
void *byte2double(long, const void *, double, double) vallocesque;
void *dct(float[8][8], float, float, float, float, float);
void *dctjpeg(float[8][8]);
double det3(const double[3][3]) nosideeffect;
void *inv3(double[restrict 3][3], const double[restrict 3][3], double);
void *matmul3(double[restrict 3][3], const double[3][3], const double[3][3]);
void *vmatmul3(double[restrict 3], const double[3], const double[3][3]);
void *matvmul3(double[restrict 3], const double[3][3], const double[3]);
double rgb2stdtv(double) pureconst;
double rgb2lintv(double) pureconst;
double rgb2stdpc(double, double) pureconst;
double rgb2linpc(double, double) pureconst;
double tv2pcgamma(double, double) pureconst;
#ifndef __cplusplus
void sad16x8n(size_t n, short[n][8], const short[n][8]);
void float2short(size_t n, short[n][8], const float[n][8]);
void scalevolume(size_t n, int16_t[n][8], int);
#endif
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_CORE_H_ */

69
dsp/core/core.mk Normal file
View file

@ -0,0 +1,69 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += DSP_CORE
DSP_CORE_ARTIFACTS += DSP_CORE_A
DSP_CORE = $(DSP_CORE_A_DEPS) $(DSP_CORE_A)
DSP_CORE_A = o/$(MODE)/dsp/core/core.a
DSP_CORE_A_FILES := $(wildcard dsp/core/*)
DSP_CORE_A_HDRS = $(filter %.h,$(DSP_CORE_A_FILES))
DSP_CORE_A_SRCS_S = $(filter %.S,$(DSP_CORE_A_FILES))
DSP_CORE_A_SRCS_C = $(filter %.c,$(DSP_CORE_A_FILES))
DSP_CORE_A_SRCS = \
$(DSP_CORE_A_SRCS_S) \
$(DSP_CORE_A_SRCS_C)
DSP_CORE_A_OBJS = \
$(DSP_CORE_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(DSP_CORE_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(DSP_CORE_A_SRCS_C:%.c=o/$(MODE)/%.o)
DSP_CORE_A_CHECKS = \
$(DSP_CORE_A).pkg \
$(DSP_CORE_A_HDRS:%=o/$(MODE)/%.ok)
DSP_CORE_A_DIRECTDEPS = \
LIBC_NEXGEN32E \
LIBC_MEM \
LIBC_TINYMATH \
LIBC_STUBS
DSP_CORE_A_DEPS := \
$(call uniq,$(foreach x,$(DSP_CORE_A_DIRECTDEPS),$($(x))))
$(DSP_CORE_A): dsp/core/ \
$(DSP_CORE_A).pkg \
$(DSP_CORE_A_OBJS)
$(DSP_CORE_A).pkg: \
$(DSP_CORE_A_OBJS) \
$(foreach x,$(DSP_CORE_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/dsp/core/dct.o \
o/$(MODE)/dsp/core/c1331.o \
o/$(MODE)/dsp/core/magikarp.o \
o/$(MODE)/dsp/core/c93654369.o \
o/$(MODE)/dsp/core/float2short.o \
o/$(MODE)/dsp/core/scalevolume.o: \
OVERRIDE_CFLAGS += \
$(MATHEMATICAL)
o/tiny/dsp/core/scalevolume.o: \
OVERRIDE_CFLAGS += \
-Os
o/$(MODE)/dsp/core/det3.o: \
OVERRIDE_CFLAGS += \
-ffast-math
DSP_CORE_LIBS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x)))
DSP_CORE_SRCS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x)_SRCS))
DSP_CORE_HDRS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x)_HDRS))
DSP_CORE_CHECKS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x)_CHECKS))
DSP_CORE_OBJS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x)_OBJS))
$(DSP_CORE_OBJS): $(BUILD_FILES) dsp/core/core.mk
.PHONY: o/$(MODE)/dsp/core
o/$(MODE)/dsp/core: $(DSP_CORE_CHECKS)

85
dsp/core/dct.c Normal file
View file

@ -0,0 +1,85 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#define DCT(A, B, C, D, E, F, G, H, T, C0, C1, C2, C3, C4) \
do { \
T z1, z2, z3, z4, z5, z11, z13; \
T t0, t1, t2, t3, t4, t5, t6, t7, t8, t10, t11, t12, t13; \
t0 = A + H; \
t7 = A - H; \
t1 = B + G; \
t6 = B - G; \
t2 = C + F; \
t5 = C - F; \
t3 = D + E; \
t4 = D - E; \
t10 = t0 + t3; \
t13 = t0 - t3; \
t11 = t1 + t2; \
t12 = t1 - t2; \
A = t10 + t11; \
E = t10 - t11; \
z1 = (t12 + t13) * C0; \
C = t13 + z1; \
G = t13 - z1; \
t10 = t4 + t5; \
t11 = t5 + t6; \
t12 = t6 + t7; \
z5 = (t10 - t12) * C1; \
z2 = t10 * C2 + z5; \
z4 = t12 * C3 + z5; \
z3 = t11 * C4; \
z11 = t7 + z3; \
z13 = t7 - z3; \
F = z13 + z2; \
D = z13 - z2; \
B = z11 + z4; \
H = z11 - z4; \
} while (0)
/**
* Performs float forward discrete cosine transform.
*
* This takes a tiny block of image data and shoves the information it
* represents into the top left corner. It can be reversed using idct().
* It matters because iterating the result in a serpentine pattern makes
* run-length delta encoding super effective. Furthermore, data downward
* rightly may be divided away for lossy compression.
*
* @cost ~100ns
*/
void *dct(float M[8][8], float c0, float c1, float c2, float c3, float c4) {
unsigned y, x;
for (y = 0; y < 8; ++y) {
DCT(M[y][0], M[y][1], M[y][2], M[y][3], M[y][4], M[y][5], M[y][6], M[y][7],
float, c0, c1, c2, c3, c4);
}
for (x = 0; x < 8; ++x) {
DCT(M[0][x], M[1][x], M[2][x], M[3][x], M[4][x], M[5][x], M[6][x], M[7][x],
float, c0, c1, c2, c3, c4);
}
return M;
}
void *dctjpeg(float M[8][8]) {
return dct(M, .707106781f, .382683433f, .541196100f, 1.306562965f,
.707106781f);
}

36
dsp/core/det3.c Normal file
View file

@ -0,0 +1,36 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#define LEIBNIZ_FORMULA(a, b, c, d, e, f, g, h, i) \
(a * e * i + b * f * g + c * d * h - c * e * g - b * d * i - a * f * h)
/**
* Computes determinant of 3×3 matrix.
* i.e. how much space is inside the cube
*
* @param 𝐀 is input matrix
* @param 𝑑 is det(𝐀)
* @return |𝐀| or 0 if degenerate
*/
double det3(const double A[3][3]) {
return LEIBNIZ_FORMULA(A[0][0], A[0][1], A[0][2], A[1][0], A[1][1], A[1][2],
A[2][0], A[2][1], A[2][2]);
}

67
dsp/core/differsumsq.c Normal file
View file

@ -0,0 +1,67 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
/**
* Computes Σ𝑥Σ𝑥Σ𝑥Σ𝑥Σ𝑥Σ𝑥(δ𝑥+δ𝑥+δ𝑥+δ𝑥+δ𝑥+δ𝑥)² over 𝐿..𝐻
*
* As soon as an Analytical Engine exists, it will necessarily
* guide the future course of the science. Whenever any result
* is sought by its aid, the question will then arise by what
* course of calculation can these results be arrived at by the
* machine in the shortest time?
*
* Charles Babbage (Life of a Philosopher, 1864)
*
* @see itu.int/rec/R-REC-BT.601/
*/
double DifferSumSq(const double D[static 6], double L, double H) {
double T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T2, T20, T21, T22,
T23, T24, T25, T26, T27, T3, T4, T5, T6, T7, T8, T9;
T2 = H * H, T3 = (H * H * H), T4 = (H * H * H * H), T5 = (H * H * H * H * H),
T6 = (H * H * H * H * H * H), T7 = -10 * H, T8 = (H * H * H * H * H * H * H),
T9 = (L * L * L * L * L * L * L * L), T10 = (L * L * L * L * L * L * L),
T11 = (L * L * L * L * L * L), T12 = (L * L * L * L * L), T13 = -45 * T2,
T14 = (L * L * L * L), T15 = 180 * T3, T16 = 120 * T2, T17 = (L * L * L),
T18 = L * L, T19 = 18 * T2, T20 = (H * H * H * H * H * H * H * H);
T21 = 45 * T4;
T22 = 3 * T9 + (-12 * H - 18) * T10 + (12 * T2 + 54 * H + 45) * T11 +
(12 * T3 - T19 - 90 * H - 60) * T12 +
(-30 * T4 - 90 * T3 + T13 + 60 * H + 45) * T14 +
(12 * T5 + 90 * T4 + T15 + T16 - 18) * T17 +
(12 * T6 + 18 * T5 - T21 - 120 * T3 - 90 * T2 - 18 * H + 3) * T18 +
(-12 * T8 - 54 * T6 - 90 * T5 - 60 * T4 + T19 + 6 * H) * L + 3 * T20 +
18 * T8 + 45 * T6 + 60 * T5 + T21 + 18 * T3 + 3 * T2;
T23 =
2 * T9 + (T7 - 13) * T10 + (20 * T2 + 55 * H + 36) * T11 +
(-22 * T3 - 93 * T2 - 126 * H - 55) * T12 +
(20 * T4 + 95 * T3 + 180 * T2 + 155 * H + 50) * T14 +
(-22 * T5 - 95 * T4 - T15 - 190 * T2 - 110 * H - 27) * T17 +
(20 * T6 + 93 * T5 + 180 * T4 + 190 * T3 + T16 + 45 * H + 8) * T18 +
(-10 * T8 - 55 * T6 - 126 * T5 - 155 * T4 - 110 * T3 + T13 + T7 - 1) * L +
2 * T20 + 13 * T8 + 36 * T6 + 55 * T5 + 50 * T4 + 27 * T3 + 8 * T2 + H;
T24 = T22 * D[3], T25 = T22 * D[2], T26 = T22 * D[1], T27 = T22 * D[0];
return (T23 * D[5] * D[5] + (T22 * D[4] + T24 + T25 + T26 + T27) * D[5] +
T23 * D[4] * D[4] + (T24 + T25 + T26 + T27) * D[4] +
T23 * D[3] * D[3] + (T25 + T26 + T27) * D[3] + T23 * D[2] * D[2] +
(T26 + T27) * D[2] + T23 * D[1] * D[1] + T22 * D[0] * D[1] +
T23 * D[0] * D[0]) /
6;
}

463
dsp/core/differsumsq8.c Normal file
View file

@ -0,0 +1,463 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#include "dsp/core/q.h"
/**
* Computes Σ𝑥Σ𝑥Σ𝑥Σ𝑥Σ𝑥Σ𝑥Σ𝑥Σ𝑥(δ𝑥+δ𝑥+δ𝑥+δ𝑥+δ𝑥+δ𝑥+δ𝑥+δ𝑥)²
*
* As soon as an Analytical Engine exists, it will necessarily
* guide the future course of the science. Whenever any result
* is sought by its aid, the question will then arise by what
* course of calculation can these results be arrived at by the
* machine in the shortest time?
*
* Charles Babbage (Life of a Philosopher, 1864)
*
*/
double DifferSumSq8(const double D[static 8], double L, double H) {
double T10, T100, T101, T102, T103, T104, T105, T106, T107, T108, T109, T11;
double T110, T111, T112, T113, T114, T115, T116, T117, T118, T119, T12, T120;
double T121, T122, T123, T124, T125, T126, T127, T128, T129, T13, T130, T131;
double T132, T133, T134, T135, T136, T137, T138, T139, T14, T140, T141, T142;
double T143, T144, T145, T146, T147, T148, T149, T15, T150, T151, T152, T153;
double T154, T155, T156, T157, T158, T159, T16, T160, T161, T162, T163, T164;
double T165, T166, T167, T168, T169, T17, T170, T171, T172, T173, T174, T175;
double T176, T177, T178, T179, T18, T180, T181, T182, T183, T184, T185, T186;
double T187, T188, T189, T19, T190, T191, T192, T193, T194, T195, T196, T197;
double T198, T199, T2, T20, T200, T201, T202, T203, T204, T205, T206, T207;
double T208, T209, T21, T210, T211, T212, T213, T214, T215, T216, T217, T218;
double T219, T22, T220, T221, T222, T223, T224, T225, T226, T227, T228, T229;
double T230, T231, T232, T233, T234, T235, T236, T237, T238, T239, T24, T240;
double T241, T242, T243, T244, T245, T246, T247, T248, T249, T25, T250, T251;
double T252, T253, T254, T255, T256, T257, T258, T259, T26, T260, T261, T262;
double T263, T264, T265, T266, T267, T268, T269, T27, T270, T271, T272, T273;
double T274, T275, T276, T277, T278, T279, T28, T280, T281, T282, T283, T284;
double T285, T286, T287, T288, T289, T29, T290, T291, T292, T293, T294, T295;
double T296, T297, T298, T299, T3, T30, T300, T301, T302, T303, T304, T305;
double T307, T308, T309, T31, T310, T311, T312, T313, T314, T315, T316, T317;
double T318, T319, T32, T320, T321, T322, T323, T324, T325, T326, T327, T328;
double T329, T33, T330, T331, T332, T333, T334, T335, T336, T337, T338, T339;
double T34, T340, T341, T342, T343, T344, T345, T346, T347, T348, T349, T35;
double T350, T351, T352, T353, T354, T355, T356, T357, T358, T359, T36, T360;
double T361, T362, T363, T364, T365, T366, T367, T368, T369, T37, T370, T371;
double T372, T373, T374, T375, T376, T377, T378, T379, T38, T380, T381, T382;
double T383, T384, T385, T386, T387, T388, T389, T39, T390, T391, T392, T393;
double T394, T395, T396, T397, T398, T399, T4, T40, T400, T401, T402, T403;
double T405, T406, T407, T41, T42, T43, T44, T45, T46, T47, T48, T49, T5, T50;
double T51, T52, T53, T54, T55, T56, T57, T58, T59, T6, T60, T61, T62, T63;
double T65, T66, T67, T68, T69, T7, T70, T71, T72, T73, T74, T75, T76, T77;
double T79, T8, T80, T81, T82, T83, T84, T85, T86, T87, T88, T89, T9, T90;
double T92, T93, T94, T95, T96, T97, T98, T99, T23, T306, T91, T78, T64, T404;
T2 = 4 * D[5], T3 = 3 * D[4], T4 = 3 * D[3], T5 = 3 * D[2], T6 = 3 * D[1],
T7 = 3 * D[0], T8 = D[7] * D[7], T9 = D[6] * D[6], T10 = -28 * D[5],
T11 = -18 * D[4], T12 = -18 * D[3], T13 = -18 * D[2], T14 = -18 * D[1],
T15 = -18 * D[0], T16 = D[5] * D[5], T17 = D[4] * D[4], T18 = D[3] * D[3],
T19 = D[2] * D[2], T20 = D[1] * D[1], T21 = D[0] * D[0], T22 = -34 * D[5],
T23 = -24 * D[4], T24 = -24 * D[3], T25 = -24 * D[2], T26 = -24 * D[1],
T27 = -24 * D[0], T28 = 84 * D[5], T29 = 39 * D[4], T30 = 39 * D[3],
T31 = 39 * D[2], T32 = 39 * D[1], T33 = 39 * D[0], T34 = 210 * D[5],
T35 = 120 * D[4], T36 = 120 * D[3], T37 = 120 * D[2], T38 = 120 * D[1],
T39 = 120 * D[0], T40 = 128 * D[5], T41 = 84 * D[4], T42 = 84 * D[3],
T43 = 84 * D[2], T44 = 84 * D[1], T45 = 84 * D[0];
T46 = -144 * D[5], T47 = (T23 + T24 + T25 + T26 + T27) * D[5],
T48 = (T24 + T25 + T26 + T27) * D[4], T49 = (T25 + T26 + T27) * D[3],
T50 = (T26 + T27) * D[2], T51 = -24 * D[0] * D[1], T52 = -552 * D[5],
T53 = -192 * D[4], T54 = -192 * D[3], T55 = -192 * D[2], T56 = -192 * D[1],
T57 = -192 * D[0], T58 = H * H, T59 = -688 * D[5], T60 = -336 * D[4],
T61 = -336 * D[3], T62 = -336 * D[2], T63 = -336 * D[1], T64 = -336 * D[0],
T65 = -280 * D[5], T66 = -168 * D[4], T67 = -168 * D[3], T68 = -168 * D[2],
T69 = -168 * D[1], T70 = -168 * D[0], T71 = 168 * D[5], T72 = -42 * D[4],
T73 = -42 * D[3], T74 = -42 * D[2], T75 = -42 * D[1], T76 = -42 * D[0],
T77 = (H * H * H), T78 = 336 * D[4], T79 = 336 * D[3], T80 = 336 * D[2],
T81 = 336 * D[1], T82 = 336 * D[0], T83 = 1568 * D[5],
T84 = 336 * D[0] * D[1], T85 = 1288 * D[5], T86 = 504 * D[4],
T87 = 504 * D[3], T88 = 504 * D[2], T89 = 504 * D[1], T90 = 504 * D[0],
T91 = 392 * D[5], T92 = 210 * D[4], T93 = 210 * D[3], T94 = 210 * D[2],
T95 = 210 * D[1], T96 = 210 * D[0], T97 = 84 * T8, T98 = 168 * D[6],
T99 = 84 * T9, T100 = 84 * T16, T101 = (T41 + T42 + T43 + T44 + T45) * D[5],
T102 = 84 * T17, T103 = (T42 + T43 + T44 + T45) * D[4], T104 = 84 * T18,
T105 = (T43 + T44 + T45) * D[3], T106 = 84 * T19, T107 = (T44 + T45) * D[2],
T108 = 84 * T20, T109 = 84 * D[0] * D[1], T110 = 84 * T21, T111 = -924 * D[5],
T112 = (T78 + T79 + T80 + T81 + T82) * D[5],
T113 = (T79 + T80 + T81 + T82) * D[4], T114 = (T80 + T81 + T82) * D[3],
T115 = (T81 + T82) * D[2], T116 = (H * H * H * H), T117 = -2128 * D[5],
T118 = -2520 * D[5], T119 = (T66 + T67 + T68 + T69 + T70) * D[5],
T120 = (T67 + T68 + T69 + T70) * D[4], T121 = (T68 + T69 + T70) * D[3],
T122 = (T69 + T70) * D[2], T123 = -168 * D[0] * D[1], T124 = -1512 * D[5],
T125 = -420 * D[4], T126 = -420 * D[3], T127 = -420 * D[2],
T128 = -420 * D[1], T129 = -420 * D[0], T130 = -364 * D[5];
T131 = T97 + (T98 + T71 + T72 + T73 + T74 + T75 + T76) * D[7] + T99 +
(T71 + T72 + T73 + T74 + T75 + T76) * D[6] + T100 +
(T72 + T73 + T74 + T75 + T76) * D[5] + T102 +
(T73 + T74 + T75 + T76) * D[4] + T104 + (T74 + T75 + T76) * D[3] +
T106 + (T75 + T76) * D[2] + T108 - 42 * D[0] * D[1] + T110;
T132 = 462 * T8, T133 = 924 * D[6], T134 = 924 * D[5], T135 = 462 * T9,
T136 = 462 * T16, T137 = (T60 + T61 + T62 + T63 + T64) * D[5],
T138 = 462 * T17, T139 = (T61 + T62 + T63 + T64) * D[4], T140 = 462 * T18,
T141 = (T62 + T63 + T64) * D[3], T142 = 462 * T19, T143 = (T63 + T64) * D[2],
T144 = 462 * T20, T145 = 462 * T21, T146 = (H * H * H * H * H),
T147 = 2240 * D[5], T148 = -840 * D[4], T149 = -840 * D[3],
T150 = -840 * D[2], T151 = -840 * D[1], T152 = -840 * D[0],
T153 = 3080 * D[5], T154 = (T148 + T149 + T150 + T151 + T152) * D[5],
T155 = (T149 + T150 + T151 + T152) * D[4], T156 = (T150 + T151 + T152) * D[3],
T157 = (T151 + T152) * D[2], T158 = -840 * D[0] * D[1], T159 = 1260 * T8,
T160 = 2520 * D[6], T161 = 2520 * D[5], T162 = 1260 * T9, T163 = 1260 * T16,
T164 = 1260 * T17, T165 = 1260 * T18, T166 = 1260 * T19, T167 = 1260 * T20,
T168 = 210 * D[0] * D[1], T169 = 1260 * T21, T170 = 168 * D[4],
T171 = 168 * D[3], T172 = 168 * D[2], T173 = 168 * D[1], T174 = 168 * D[0],
T175 = 1148 * D[5], T176 = 168 * D[0] * D[1], T177 = 224 * D[5];
T178 = -72 * T8 + (-144 * D[6] + T46 + T23 + T24 + T25 + T26 + T27) * D[7] -
72 * T9 + (T46 + T23 + T24 + T25 + T26 + T27) * D[6] - 72 * T16 + T47 -
72 * T17 + T48 - 72 * T18 + T49 - 72 * T19 + T50 - 72 * T20 + T51 -
72 * T21;
T179 = 420 * T8, T180 = 840 * D[6], T181 = 840 * D[5], T182 = 420 * T9,
T183 = 840 * D[5] * D[6], T184 = 420 * T16, T185 = 420 * T17,
T186 = 420 * T18, T187 = 420 * T19, T188 = 420 * T20, T189 = 420 * T21,
T190 = (H * H * H * H * H * H);
T191 = -1064 * T8 +
(-2128 * D[6] + T117 + T78 + T79 + T80 + T81 + T82) * D[7] -
1064 * T9 + (T117 + T78 + T79 + T80 + T81 + T82) * D[6] - 1064 * T16 +
T112 - 1064 * T17 + T113 - 1064 * T18 + T114 - 1064 * T19 + T115 -
1064 * T20 + T84 - 1064 * T21;
T192 = 1540 * T8, T193 = 3080 * D[6], T194 = 840 * D[4], T195 = 840 * D[3],
T196 = 840 * D[2], T197 = 840 * D[1], T198 = 840 * D[0], T199 = 1540 * T9,
T200 = 1540 * T16, T201 = 1540 * T17, T202 = 1540 * T18, T203 = 1540 * T19,
T204 = 1540 * T20, T205 = 840 * D[0] * D[1], T206 = 1540 * T21,
T207 = -2800 * D[5], T208 = (T194 + T195 + T196 + T197 + T198) * D[5],
T209 = (T195 + T196 + T197 + T198) * D[4], T210 = (T196 + T197 + T198) * D[3],
T211 = (T197 + T198) * D[2], T212 = -1624 * D[5], T213 = -88 * D[5];
T214 = 42 * T8 + (84 * D[6] + T28 + T29 + T30 + T31 + T32 + T33) * D[7] +
42 * T9 + (T28 + T29 + T30 + T31 + T32 + T33) * D[6] + 42 * T16 +
(T29 + T30 + T31 + T32 + T33) * D[5] + 42 * T17 +
(T30 + T31 + T32 + T33) * D[4] + 42 * T18 + (T31 + T32 + T33) * D[3] +
42 * T19 + (T32 + T33) * D[2] + 42 * T20 + 39 * D[0] * D[1] + 42 * T21;
T215 = 276 * T8, T216 = 552 * D[6], T217 = 552 * D[5], T218 = 192 * D[4],
T219 = 192 * D[3], T220 = 192 * D[2], T221 = 192 * D[1], T222 = 192 * D[0],
T223 = 276 * T9, T224 = 276 * T16, T225 = 276 * T17, T226 = 276 * T18,
T227 = 276 * T19, T228 = 276 * T20, T229 = 192 * D[0] * D[1],
T230 = 276 * T21, T231 = (H * H * H * H * H * H * H);
T232 = 784 * T8 + (1568 * D[6] + T83 + T78 + T79 + T80 + T81 + T82) * D[7] +
784 * T9 + (T83 + T78 + T79 + T80 + T81 + T82) * D[6] + 784 * T16 +
T112 + 784 * T17 + T113 + 784 * T18 + T114 + 784 * T19 + T115 +
784 * T20 + T84 + 784 * T21;
T233 = (T170 + T171 + T172 + T173 + T174) * D[5];
T234 = (T171 + T172 + T173 + T174) * D[4];
T235 = (T172 + T173 + T174) * D[3];
T236 = (T173 + T174) * D[2];
T237 = T159 + (T160 + T161 - T92 - T93 - T94 - T95 - T96) * D[7] + T162 +
(T161 - T92 - T93 - T94 - T95 - T96) * D[6] + T163 +
(-T92 - T93 - T94 - T95 - T96) * D[5] + T164 +
(-T93 - T94 - T95 - T96) * D[4] + T165 + (-T94 - T95 - T96) * D[3] +
T166 + (-T95 - T96) * D[2] + T167 - T168 + T169;
T238 = 812 * T8, T239 = 1624 * D[6], T240 = 1624 * D[5], T241 = 812 * T9,
T242 = 812 * T16, T243 = 812 * T17, T244 = 812 * T18, T245 = 812 * T19,
T246 = 812 * T20, T247 = 812 * T21, T248 = 672 * D[5], T249 = 20 * D[5],
T250 = (T3 + T4 + T5 + T6 + T7) * D[5], T251 = (T4 + T5 + T6 + T7) * D[4],
T252 = (T5 + T6 + T7) * D[3], T253 = (T6 + T7) * D[2], T254 = 3 * D[0] * D[1];
T255 = -14 * T8 + (-28 * D[6] + T10 + T11 + T12 + T13 + T14 + T15) * D[7] -
14 * T9 + (T10 + T11 + T12 + T13 + T14 + T15) * D[6] - 14 * T16 +
(T11 + T12 + T13 + T14 + T15) * D[5] - 14 * T17 +
(T12 + T13 + T14 + T15) * D[4] - 14 * T18 + (T13 + T14 + T15) * D[3] -
14 * T19 + (T14 + T15) * D[2] - 14 * T20 - 18 * D[0] * D[1] - 14 * T21;
T256 = 105 * T8, T257 = 210 * D[6], T258 = 105 * T9, T259 = 105 * T16,
T260 = 105 * T17, T261 = 105 * T18, T262 = 105 * T19, T263 = 105 * T20,
T264 = 120 * D[0] * D[1], T265 = 105 * T21,
T266 = (H * H * H * H * H * H * H * H);
T267 = -344 * T8 + (-688 * D[6] + T59 + T60 + T61 + T62 + T63 + T64) * D[7] -
344 * T9 + (T59 + T60 + T61 + T62 + T63 + T64) * D[6] - 344 * T16 +
T137 - 344 * T17 + T139 - 344 * T18 + T141 - 344 * T19 + T143 -
344 * T20 - T84 - 344 * T21;
T268 = 644 * T8, T269 = 1288 * D[6], T270 = 644 * T9, T271 = 644 * T16,
T272 = 644 * T17, T273 = 644 * T18, T274 = 644 * T19, T275 = 644 * T20,
T276 = 504 * D[0] * D[1], T277 = 644 * T21;
T278 = -756 * T8 +
(-1512 * D[6] + T124 + T125 + T126 + T127 + T128 + T129) * D[7] -
756 * T9 + (T124 + T125 + T126 + T127 + T128 + T129) * D[6] -
756 * T16 + (T125 + T126 + T127 + T128 + T129) * D[5] - 756 * T17 +
(T126 + T127 + T128 + T129) * D[4] - 756 * T18 +
(T127 + T128 + T129) * D[3] - 756 * T19 + (T128 + T129) * D[2] -
756 * T20 - 420 * D[0] * D[1] - 756 * T21;
T279 = 574 * T8, T280 = 1148 * D[6], T281 = 574 * T9, T282 = 574 * T16,
T283 = 574 * T17, T284 = 574 * T18, T285 = 574 * T19, T286 = 574 * T20,
T287 = 574 * T21;
T288 = -280 * T8 + (-560 * D[6] - 560 * D[5]) * D[7] - 280 * T9 -
560 * D[5] * D[6] - 280 * T16 - 280 * T17 - 280 * T18 - 280 * T19 -
280 * T20 - 280 * T21;
T289 = 24 * D[4], T290 = 24 * D[3], T291 = 24 * D[2], T292 = 24 * D[1],
T293 = 24 * D[0], T294 = 24 * D[0] * D[1], T295 = -14 * T8, T296 = -28 * D[6],
T297 = -14 * T9, T298 = 6 * D[4], T299 = 6 * D[3], T300 = 6 * D[2],
T301 = 6 * D[1], T302 = 6 * D[0], T303 = -14 * T16, T304 = -14 * T17,
T305 = -14 * T18, T306 = -14 * T19, T307 = -14 * T20, T308 = -14 * T21;
T309 = 2 * T8 + (4 * D[6] + T2 + T3 + T4 + T5 + T6 + T7) * D[7] + 2 * T9 +
(T2 + T3 + T4 + T5 + T6 + T7) * D[6] + 2 * T16 + T250 + 2 * T17 +
T251 + 2 * T18 + T252 + 2 * T19 + T253 + 2 * T20 + T254 + 2 * T21;
T310 = 17 * T8, T311 = 34 * D[6], T312 = 34 * D[5], T313 = 17 * T9,
T314 = 17 * T16, T315 = (T289 + T290 + T291 + T292 + T293) * D[5];
T316 = 17 * T17, T317 = (T290 + T291 + T292 + T293) * D[4];
T318 = 17 * T18, T319 = (T291 + T292 + T293) * D[3];
T320 = 17 * T19, T321 = (T292 + T293) * D[2];
T322 = 17 * T20, T323 = 17 * T21, T324 = (H * H * H * H * H * H * H * H * H);
T325 = 64 * T8,
T326 = (128 * D[6] + T40 + T41 + T42 + T43 + T44 + T45) * D[7];
T327 = 64 * T9, T328 = (T40 + T41 + T42 + T43 + T44 + T45) * D[6];
T329 = 64 * T16, T330 = 64 * T17, T331 = 64 * T18, T332 = 64 * T19,
T333 = 64 * T20, T334 = 64 * T21, T335 = 140 * T8, T336 = 280 * D[6],
T337 = 280 * D[5], T338 = 140 * T9, T339 = 140 * T16, T340 = 140 * T17,
T341 = 140 * T18, T342 = 140 * T19, T343 = 140 * T20, T344 = 140 * T21,
T345 = 196 * T8;
T346 = (392 * D[6] + T91 + T92 + T93 + T94 + T95 + T96) * D[7];
T347 = 196 * T9;
T348 = (T91 + T92 + T93 + T94 + T95 + T96) * D[6];
T349 = 196 * T16;
T350 = (T92 + T93 + T94 + T95 + T96) * D[5];
T351 = 196 * T17;
T352 = (T93 + T94 + T95 + T96) * D[4];
T353 = 196 * T18;
T354 = (T94 + T95 + T96) * D[3];
T355 = 196 * T19;
T356 = (T95 + T96) * D[2];
T357 = 196 * T20, T358 = 196 * T21, T359 = 182 * T8, T360 = 364 * D[6],
T361 = 364 * D[5], T362 = 182 * T9, T363 = 182 * T16, T364 = 182 * T17,
T365 = 182 * T18, T366 = 182 * T19, T367 = 182 * T20, T368 = 182 * T21,
T369 = 112 * T8;
T370 = (224 * D[6] + T177 + T41 + T42 + T43 + T44 + T45) * D[7];
T371 = 112 * T9;
T372 = (T177 + T41 + T42 + T43 + T44 + T45) * D[6];
T373 = 112 * T16, T374 = 112 * T17, T375 = 112 * T18, T376 = 112 * T19,
T377 = 112 * T20, T378 = 112 * T21, T379 = 44 * T8, T380 = 88 * D[6],
T381 = 88 * D[5], T382 = 44 * T9, T383 = 44 * T16, T384 = 44 * T17,
T385 = 44 * T18, T386 = 44 * T19, T387 = 44 * T20, T388 = 44 * T21,
T389 = 10 * T8, T390 = (20 * D[6] + T249 + T3 + T4 + T5 + T6 + T7) * D[7];
T391 = 10 * T9, T392 = (T249 + T3 + T4 + T5 + T6 + T7) * D[6];
T393 = 10 * T16, T394 = 10 * T17, T395 = 10 * T18, T396 = 10 * T19,
T397 = 10 * T20, T398 = 10 * T21, T399 = 2 * D[6], T400 = 2 * D[5],
T401 = 2 * D[5] * D[6];
T402 = T309 * (L * L * L * L * L * L * L * L * L * L) +
(T255 * H - T310 + (-T311 + T22 + T23 + T24 + T25 + T26 + T27) * D[7] -
T313 + (T22 + T23 + T24 + T25 + T26 + T27) * D[6] - T314 + T47 -
T316 + T48 - T318 + T49 - T320 + T50 - T322 + T51 - T323) *
(L * L * L * L * L * L * L * L * L) +
(T214 * T58 +
(T256 + (T257 + T34 + T35 + T36 + T37 + T38 + T39) * D[7] + T258 +
(T34 + T35 + T36 + T37 + T38 + T39) * D[6] + T259 +
(T35 + T36 + T37 + T38 + T39) * D[5] + T260 +
(T36 + T37 + T38 + T39) * D[4] + T261 + (T37 + T38 + T39) * D[3] +
T262 + (T38 + T39) * D[2] + T263 + T264 + T265) *
H +
T325 + T326 + T327 + T328 + T329 + T101 + T330 + T103 + T331 + T105 +
T332 + T107 + T333 + T109 + T334) *
(L * L * L * L * L * L * L * L) +
(T178 * T77 +
(-T215 + (-T216 + T52 + T53 + T54 + T55 + T56 + T57) * D[7] - T223 +
(T52 + T53 + T54 + T55 + T56 + T57) * D[6] - T224 +
(T53 + T54 + T55 + T56 + T57) * D[5] - T225 +
(T54 + T55 + T56 + T57) * D[4] - T226 + (T55 + T56 + T57) * D[3] -
T227 + (T56 + T57) * D[2] - T228 - T229 - T230) *
T58 +
T267 * H - T335 + (-T336 + T65 + T66 + T67 + T68 + T69 + T70) * D[7] -
T338 + (T65 + T66 + T67 + T68 + T69 + T70) * D[6] - T339 + T119 -
T340 + T120 - T341 + T121 - T342 + T122 - T343 + T123 - T344) *
(L * L * L * L * L * L * L);
T403 =
T402 +
(T131 * T116 +
(T179 + (T180 + T181) * D[7] + T182 + T183 + T184 + T185 + T186 + T187 +
T188 + T189) *
T77 +
T232 * T58 +
(T268 + (T269 + T85 + T86 + T87 + T88 + T89 + T90) * D[7] + T270 +
(T85 + T86 + T87 + T88 + T89 + T90) * D[6] + T271 +
(T86 + T87 + T88 + T89 + T90) * D[5] + T272 +
(T87 + T88 + T89 + T90) * D[4] + T273 + (T88 + T89 + T90) * D[3] +
T274 + (T89 + T90) * D[2] + T275 + T276 + T277) *
H +
T345 + T346 + T347 + T348 + T349 + T350 + T351 + T352 + T353 + T354 +
T355 + T356 + T357 + T168 + T358) *
(L * L * L * L * L * L) +
((-T97 + (-T98 - T71 + T41 + T42 + T43 + T44 + T45) * D[7] - T99 +
(-T71 + T41 + T42 + T43 + T44 + T45) * D[6] - T100 + T101 - T102 +
T103 - T104 + T105 - T106 + T107 - T108 + T109 - T110) *
T146 +
(-T132 + (-T133 + T111 + T78 + T79 + T80 + T81 + T82) * D[7] - T135 +
(T111 + T78 + T79 + T80 + T81 + T82) * D[6] - T136 + T112 - T138 +
T113 - T140 + T114 - T142 + T115 - T144 + T84 - T145) *
T116 +
T191 * T77 +
(-T159 + (-T160 + T118 + T66 + T67 + T68 + T69 + T70) * D[7] - T162 +
(T118 + T66 + T67 + T68 + T69 + T70) * D[6] - T163 + T119 - T164 +
T120 - T165 + T121 - T166 + T122 - T167 + T123 - T169) *
T58 +
T278 * H - T359 + (-T360 + T130 + T66 + T67 + T68 + T69 + T70) * D[7] -
T362 + (T130 + T66 + T67 + T68 + T69 + T70) * D[6] - T363 + T119 - T364 +
T120 - T365 + T121 - T366 + T122 - T367 + T123 - T368) *
(L * L * L * L * L);
T404 =
T403 +
(T131 * T190 +
(T132 + (T133 + T134 + T60 + T61 + T62 + T63 + T64) * D[7] + T135 +
(T134 + T60 + T61 + T62 + T63 + T64) * D[6] + T136 + T137 + T138 +
T139 + T140 + T141 + T142 + T143 + T144 - T84 + T145) *
T146 +
(1120 * T8 +
(2240 * D[6] + T147 + T148 + T149 + T150 + T151 + T152) * D[7] +
1120 * T9 + (T147 + T148 + T149 + T150 + T151 + T152) * D[6] +
1120 * T16 + T154 + 1120 * T17 + T155 + 1120 * T18 + T156 + 1120 * T19 +
T157 + 1120 * T20 + T158 + 1120 * T21) *
T116 +
(T192 + (T193 + T153 + T148 + T149 + T150 + T151 + T152) * D[7] + T199 +
(T153 + T148 + T149 + T150 + T151 + T152) * D[6] + T200 + T154 + T201 +
T155 + T202 + T156 + T203 + T157 + T204 + T158 + T206) *
T77 +
T237 * T58 +
(T279 + (T280 + T175 + T170 + T171 + T172 + T173 + T174) * D[7] + T281 +
(T175 + T170 + T171 + T172 + T173 + T174) * D[6] + T282 + T233 + T283 +
T234 + T284 + T235 + T285 + T236 + T286 + T176 + T287) *
H +
T369 + T370 + T371 + T372 + T373 + T101 + T374 + T103 + T375 + T105 +
T376 + T107 + T377 + T109 + T378) *
(L * L * L * L) +
(T178 * T231 +
(-T179 + (-T180 - T181) * D[7] - T182 - T183 - T184 - T185 - T186 -
T187 - T188 - T189) *
T190 +
T191 * T146 +
(-T192 + (-T193 - T153 + T194 + T195 + T196 + T197 + T198) * D[7] -
T199 + (-T153 + T194 + T195 + T196 + T197 + T198) * D[6] - T200 + T208 -
T201 + T209 - T202 + T210 - T203 + T211 - T204 + T205 - T206) *
T116 +
(-1400 * T8 +
(-2800 * D[6] + T207 + T194 + T195 + T196 + T197 + T198) * D[7] -
1400 * T9 + (T207 + T194 + T195 + T196 + T197 + T198) * D[6] -
1400 * T16 + T208 - 1400 * T17 + T209 - 1400 * T18 + T210 - 1400 * T19 +
T211 - 1400 * T20 + T205 - 1400 * T21) *
T77 +
(-T238 + (-T239 + T212 + T78 + T79 + T80 + T81 + T82) * D[7] - T241 +
(T212 + T78 + T79 + T80 + T81 + T82) * D[6] - T242 + T112 - T243 +
T113 - T244 + T114 - T245 + T115 - T246 + T84 - T247) *
T58 +
T288 * H - T379 + (-T380 + T213 + T23 + T24 + T25 + T26 + T27) * D[7] -
T382 + (T213 + T23 + T24 + T25 + T26 + T27) * D[6] - T383 + T47 - T384 +
T48 - T385 + T49 - T386 + T50 - T387 + T51 - T388) *
(L * L * L);
T405 =
T214 * T266 +
(T215 + (T216 + T217 + T218 + T219 + T220 + T221 + T222) * D[7] + T223 +
(T217 + T218 + T219 + T220 + T221 + T222) * D[6] + T224 +
(T218 + T219 + T220 + T221 + T222) * D[5] + T225 +
(T219 + T220 + T221 + T222) * D[4] + T226 + (T220 + T221 + T222) * D[3] +
T227 + (T221 + T222) * D[2] + T228 + T229 + T230) *
T231 +
T232 * T190 +
(T159 + (T160 + T161 + T170 + T171 + T172 + T173 + T174) * D[7] + T162 +
(T161 + T170 + T171 + T172 + T173 + T174) * D[6] + T163 + T233 + T164 +
T234 + T165 + T235 + T166 + T236 + T167 + T176 + T169) *
T146 +
T237 * T116 +
(T238 + (T239 + T240 + T60 + T61 + T62 + T63 + T64) * D[7] + T241 +
(T240 + T60 + T61 + T62 + T63 + T64) * D[6] + T242 + T137 + T243 + T139 +
T244 + T141 + T245 + T143 + T246 - T84 + T247) *
T77 +
(336 * T8 + (672 * D[6] + T248 + T66 + T67 + T68 + T69 + T70) * D[7] +
336 * T9 + (T248 + T66 + T67 + T68 + T69 + T70) * D[6] + 336 * T16 +
T119 + 336 * T17 + T120 + 336 * T18 + T121 + 336 * T19 + T122 +
336 * T20 - T176 + 336 * T21) *
T58 +
(T97 + (T98 + T71 + T23 + T24 + T25 + T26 + T27) * D[7] + T99 +
(T71 + T23 + T24 + T25 + T26 + T27) * D[6] + T100 + T47 + T102 + T48 +
T104 + T49 + T106 + T50 + T108 + T51 + T110) *
H +
T389 + T390 + T391;
T406 = T255 * T324 +
(-T256 + (-T257 - T34 - T35 - T36 - T37 - T38 - T39) * D[7] - T258 +
(-T34 - T35 - T36 - T37 - T38 - T39) * D[6] - T259 +
(-T35 - T36 - T37 - T38 - T39) * D[5] - T260 +
(-T36 - T37 - T38 - T39) * D[4] - T261 + (-T37 - T38 - T39) * D[3] -
T262 + (-T38 - T39) * D[2] - T263 - T264 - T265) *
T266 +
T267 * T231 +
(-T268 + (-T269 - T85 - T86 - T87 - T88 - T89 - T90) * D[7] - T270 +
(-T85 - T86 - T87 - T88 - T89 - T90) * D[6] - T271 +
(-T86 - T87 - T88 - T89 - T90) * D[5] - T272 +
(-T87 - T88 - T89 - T90) * D[4] - T273 + (-T88 - T89 - T90) * D[3] -
T274 + (-T89 - T90) * D[2] - T275 - T276 - T277) *
T190 +
T278 * T146 +
(-T279 + (-T280 - T175 + T66 + T67 + T68 + T69 + T70) * D[7] - T281 +
(-T175 + T66 + T67 + T68 + T69 + T70) * D[6] - T282 + T119 - T283 +
T120 - T284 + T121 - T285 + T122 - T286 - T176 - T287) *
T116 +
T288 * T77 +
(-T97 + (-T98 - T71 + T289 + T290 + T291 + T292 + T293) * D[7] - T99 +
(-T71 + T289 + T290 + T291 + T292 + T293) * D[6] - T100 + T315 -
T102 + T317 - T104 + T319 - T106 + T321 - T108 + T294 - T110) *
T58;
T407 = T404 +
(T405 + T392 + T393 + T250 + T394 + T251 + T395 + T252 + T396 + T253 +
T397 + T254 + T398) *
L * L +
(T406 +
(T295 + (T296 + T10 + T298 + T299 + T300 + T301 + T302) * D[7] +
T297 + (T10 + T298 + T299 + T300 + T301 + T302) * D[6] + T303 +
(T298 + T299 + T300 + T301 + T302) * D[5] + T304 +
(T299 + T300 + T301 + T302) * D[4] + T305 +
(T300 + T301 + T302) * D[3] + T306 + (T301 + T302) * D[2] + T307 +
6 * D[0] * D[1] + T308) *
H -
T8 + (-T399 - T400) * D[7] - T9 - T401 - T16 - T17 - T18 - T19 - T20 -
T21) *
L +
T309 * (H * H * H * H * H * H * H * H * H * H) +
(T310 + (T311 + T312 + T289 + T290 + T291 + T292 + T293) * D[7] +
T313 + (T312 + T289 + T290 + T291 + T292 + T293) * D[6] + T314 +
T315 + T316 + T317 + T318 + T319 + T320 + T321 + T322 + T294 + T323) *
T324 +
(T325 + T326 + T327 + T328 + T329 + T101 + T330 + T103 + T331 + T105 +
T332 + T107 + T333 + T109 + T334) *
T266 +
(T335 + (T336 + T337 + T170 + T171 + T172 + T173 + T174) * D[7] +
T338 + (T337 + T170 + T171 + T172 + T173 + T174) * D[6] + T339 +
T233 + T340 + T234 + T341 + T235 + T342 + T236 + T343 + T176 + T344) *
T231 +
(T345 + T346 + T347 + T348 + T349 + T350 + T351 + T352 + T353 + T354 +
T355 + T356 + T357 + T168 + T358) *
T190;
return (T407 +
(T359 + (T360 + T361 + T170 + T171 + T172 + T173 + T174) * D[7] +
T362 + (T361 + T170 + T171 + T172 + T173 + T174) * D[6] + T363 +
T233 + T364 + T234 + T365 + T235 + T366 + T236 + T367 + T176 +
T368) *
T146 +
(T369 + T370 + T371 + T372 + T373 + T101 + T374 + T103 + T375 + T105 +
T376 + T107 + T377 + T109 + T378) *
T116 +
(T379 + (T380 + T381 + T289 + T290 + T291 + T292 + T293) * D[7] +
T382 + (T381 + T289 + T290 + T291 + T292 + T293) * D[6] + T383 +
T315 + T384 + T317 + T385 + T319 + T386 + T321 + T387 + T294 +
T388) *
T77 +
(T389 + T390 + T391 + T392 + T393 + T250 + T394 + T251 + T395 + T252 +
T396 + T253 + T397 + T254 + T398) *
T58 +
(T8 + (T399 + T400) * D[7] + T9 + T401 + T16 + T17 + T18 + T19 + T20 +
T21) *
H) /
6;
}

35
dsp/core/double2byte.c Normal file
View file

@ -0,0 +1,35 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
void *double2byte(long n, const void *p, double weight, double bias) {
long i;
const double *src;
unsigned char *dst;
if ((dst = valloc(n * sizeof(unsigned char)))) {
for (src = p, i = 0; i < n; ++i) {
dst[i] = MIN(255, MAX(0, rint(src[i] * weight + bias)));
}
}
return dst;
}

41
dsp/core/float2short.c Normal file
View file

@ -0,0 +1,41 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/limits.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/str/str.h"
/**
* Converts floating point audio samples to pulse code modulation.
*
* @param rdi is number of sample chunks
* @param rsi points to aligned pcm [-32k,+32k] output memory
* @param rdx points to aligned float32 [-1,+1] input memory
*/
void float2short(size_t n, short pcm16[n][8], const float binary32[n][8]) {
size_t i, j;
float f[8], w[8];
for (i = 0; i < n; ++i) {
for (j = 0; j < 8; ++j) {
pcm16[i][j] =
MIN(SHRT_MAX, MAX(SHRT_MIN, floorf(binary32[i][j] * (SHRT_MAX + 1))));
}
}
}

40
dsp/core/gamma.c Normal file
View file

@ -0,0 +1,40 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#include "dsp/core/gamma.h"
#include "libc/math.h"
double rgb2stdpc(double x, double g) {
return COMPANDLUMA_SRGB(x, g);
}
double rgb2linpc(double x, double g) {
return UNCOMPANDLUMA_SRGB(x, g);
}
double rgb2stdtv(double x) {
return COMPANDLUMA_BT1886(x);
}
double rgb2lintv(double x) {
return UNCOMPANDLUMA_BT1886(x);
}
double tv2pcgamma(double x, double g) {
return rgb2stdpc(rgb2lintv(x), g);
}

23
dsp/core/gamma.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef COSMOPOLITAN_DSP_CORE_GAMMA_H_
#define COSMOPOLITAN_DSP_CORE_GAMMA_H_
#include "libc/math.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#define COMPANDLUMA(X, ...) COMPANDLUMA_(X, __VA_ARGS__)
#define COMPANDLUMA_(X, K1, K2, K3, K4) \
((X) > (K3) / (K4) ? (1 + (K2)) * pow((X), 1 / (K1)) - (K2) : (X) * (K4))
#define UNCOMPANDLUMA(X, ...) UNCOMPANDLUMA_(X, __VA_ARGS__)
#define UNCOMPANDLUMA_(X, K1, K2, K3, K4) \
((X) > (K3) ? pow(1 / (1 + (K2)) * ((X) + (K2)), K1) : (X) / (K4))
#define COMPANDLUMA_SRGB_MAGNUM .055, .04045, 12.92
#define COMPANDLUMA_SRGB(X, G) COMPANDLUMA(X, G, COMPANDLUMA_SRGB_MAGNUM)
#define UNCOMPANDLUMA_SRGB(X, G) UNCOMPANDLUMA(X, G, COMPANDLUMA_SRGB_MAGNUM)
#define COMPANDLUMA_BT1886_MAGNUM 1 / .45, .099, .081, 4.5
#define COMPANDLUMA_BT1886(X) COMPANDLUMA(X, COMPANDLUMA_BT1886_MAGNUM)
#define UNCOMPANDLUMA_BT1886(X) UNCOMPANDLUMA(X, COMPANDLUMA_BT1886_MAGNUM)
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_GAMMA_H_ */

View file

@ -0,0 +1,113 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/q.h"
#include "libc/assert.h"
#include "libc/dce.h"
#include "libc/limits.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/str/str.h"
/**
* Precomputes integers that can replace floating-point operands.
*
* G-d made the integers, all else is the work of man.
* Leopold Kronecker
*
* This function shifts the decimal point to the left:
*
* 𝑛 ROUND[𝑐 × 2] + φ
*
* With extra effort to compute φ which is normally all zeroes but gives
* us better rounding when it isn't. It's assumed optimized coefficients
* will be used like this:
*
* (Σ𝑥𝑛 + 2¹) / 2 where 𝑥[𝐿,𝐻] and 𝑖[0,6)
*
* Intended to compute this
*
* ROUND[Σ𝑥𝑐]
*
* As accurately or approximately as you want it to be. Popular scaling
* factors are 7, 15, 16, 22, and 31. Building this code under MODE=tiny
* will DCE the math.
*
* @param N receives optimized integers
* @param C provides ideal coefficients
* @param M is log scaling factor, e.g. 7
* @param L is minimum input data size, e.g. 0
* @param H is maximum input data size, e.g. 255
* @return sum of errors for all inputs
* @see en.wikipedia.org/wiki/Binary_scaling
* @see o/tool/build/coefficients.com
* @cost ~300ns
*/
long GetIntegerCoefficients(long N[static 6], const double C[static 6], long M,
long L, long H) {
int i;
int j[6], J[6];
int O[6] = {0};
int S[3] = {0, -1, +1};
double R[6], K[6], D[6], HM, HL, least, error;
least = 1;
HM = 1L << M;
HL = H - L + 1;
assert(H >= L);
assert(HL <= HM);
for (i = 0; i < 6; ++i) {
least *= HL;
if (fabs(C[i]) > DBL_MIN) {
J[i] = ARRAYLEN(S);
R[i] = C[i] * HM;
K[i] = rint(R[i]);
N[i] = K[i];
} else {
J[i] = 1;
R[i] = 0;
K[i] = 0;
N[i] = 0;
}
}
if (!IsTiny() && least > 1) {
for (j[0] = 0; j[0] < J[0]; ++j[0]) {
for (j[1] = 0; j[1] < J[1]; ++j[1]) {
for (j[2] = 0; j[2] < J[2]; ++j[2]) {
for (j[3] = 0; j[3] < J[3]; ++j[3]) {
for (j[4] = 0; j[4] < J[4]; ++j[4]) {
for (j[5] = 0; j[5] < J[5]; ++j[5]) {
for (i = 0; i < ARRAYLEN(J); ++i) {
D[i] = S[j[i]] + K[i] - R[i];
}
if ((error = DifferSumSq(D, L, H) / HM) < least) {
least = error;
memcpy(O, j, sizeof(j));
}
}
}
}
}
}
}
for (i = 0; i < 6; ++i) {
N[i] += S[O[i]];
}
}
return lround(least);
}

View file

@ -0,0 +1,83 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#include "dsp/core/q.h"
#include "libc/dce.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/str/str.h"
/**
* Same as GetIntegerCoefficients() but with eight terms.
* @cost ~3ms
*/
long GetIntegerCoefficients8(long N[static 8], const double C[static 8], long M,
long L, long H) {
int i;
int j[8], J[8];
int O[8] = {0};
int S[3] = {0, -1, +1};
double R[8], K[8], D[8], Z, least, error;
least = 1;
Z = 1L << M;
for (i = 0; i < ARRAYLEN(J); ++i) {
least *= H - L;
if (fabs(C[i]) > DBL_MIN) {
J[i] = ARRAYLEN(S);
R[i] = C[i] * Z;
K[i] = rint(R[i]);
N[i] = K[i];
} else {
J[i] = 1;
R[i] = 0;
K[i] = 0;
N[i] = 0;
}
}
if (least > 1) {
for (j[0] = 0; j[0] < J[0]; ++j[0]) {
for (j[1] = 0; j[1] < J[1]; ++j[1]) {
for (j[2] = 0; j[2] < J[2]; ++j[2]) {
for (j[3] = 0; j[3] < J[3]; ++j[3]) {
for (j[4] = 0; j[4] < J[4]; ++j[4]) {
for (j[5] = 0; j[5] < J[5]; ++j[5]) {
for (j[6] = 0; j[6] < J[6]; ++j[6]) {
for (j[7] = 0; j[7] < J[7]; ++j[7]) {
for (i = 0; i < ARRAYLEN(J); ++i) {
D[i] = S[j[i]] + K[i] - R[i];
}
if ((error = DifferSumSq8(D, L, H) / Z) < least) {
least = error;
memcpy(O, j, sizeof(j));
}
}
}
}
}
}
}
}
}
for (i = 0; i < 8; ++i) {
N[i] += S[O[i]];
}
}
return lround(least);
}

12
dsp/core/half.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef COSMOPOLITAN_DSP_CORE_HALF_H_
#define COSMOPOLITAN_DSP_CORE_HALF_H_
#include "libc/macros.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* Divides integer in half w/ rounding.
*/
#define HALF(X) (((X) + 1) / (2 / TYPE_INTEGRAL(typeof(X))))
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_HALF_H_ */

79
dsp/core/illumination.c Normal file
View file

@ -0,0 +1,79 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#include "dsp/core/illumination.h"
#include "libc/str/str.h"
const double kIlluminantA[3] = {1.0985, 1, 0.35585};
const double kIlluminantAD10[3] = {1.11144, 1, 0.35200};
const double kIlluminantC[3] = {0.98074, 1, 1.18232};
const double kIlluminantCD10[3] = {0.97285, 1, 1.16145};
const double kIlluminantD50[3] = {0.96422, 1, 0.82521};
const double kIlluminantD50D10[3] = {0.9672, 1, 0.81427};
const double kIlluminantD55[3] = {0.95682, 1, 0.92149};
const double kIlluminantD55D10[3] = {0.95799, 1, 0.90926};
const double kIlluminantD65[3] = {0.95047, 1, 1.08883};
const double kIlluminantD65D10[3] = {0.94811, 1, 1.07304};
const double kIlluminantD75[3] = {0.94972, 1, 1.22638};
const double kIlluminantD75D10[3] = {0.94416, 1, 1.20641};
const double kIlluminantF2[3] = {0.99187, 1, 0.67395};
const double kIlluminantF2D10[3] = {1.0328, 1, 0.69026};
const double kIlluminantF7[3] = {0.95044, 1, 1.08755};
const double kIlluminantF7D10[3] = {0.95792, 1, 1.07687};
const double kIlluminantF11[3] = {1.00966, 1, 0.6437};
const double kIlluminantF11D10[3] = {1.03866, 1, 0.65627};
/**
* System of equations used for changing illumination.
*
* @see brucelindbloom.com/Eqn_ChromAdapt.html The Bradford method is
* the newest of the three methods, and is considered by most
* experts to be the best of them. This is the method used in Adobe
* Photoshop. A related article comparing the chromatic adaptation
* algorithms may be found here. Quoth Bruce Lindbloom
*/
const double kBradford[3][3] = {
{0.8951, 0.2664, -.1614},
{-.7502, 1.7135, 0.0367},
{0.0389, -.0685, 1.0296},
};
/**
* Computes lightbulb changing coefficients.
*
* @param R will store result
* @param S has intended input illuminant primaries
* @param D has desired output illuminant primaries
* @return R
* @see brucelindbloom.com/Eqn_ChromAdapt.html
* @see fluxometer.com/rainbow/
*/
void *GetChromaticAdaptationMatrix(double R[3][3], const double S[3],
const double D[3]) {
double M[3][3], T[3][3], U[3][3], V[3], W[3];
matvmul3(V, kBradford, S);
matvmul3(W, kBradford, D);
memset(M, 0, sizeof(M));
M[0][0] = W[0] / V[0];
M[1][1] = W[1] / V[1];
M[2][2] = W[2] / V[2];
return matmul3(R, matmul3(T, kBradford, M),
inv3(U, kBradford, det3(kBradford)));
}

31
dsp/core/illumination.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef COSMOPOLITAN_DSP_CORE_ILLUMINANT_H_
#define COSMOPOLITAN_DSP_CORE_ILLUMINANT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const double kBradford[3][3];
extern const double kIlluminantA[3];
extern const double kIlluminantAD10[3];
extern const double kIlluminantC[3];
extern const double kIlluminantCD10[3];
extern const double kIlluminantD50[3];
extern const double kIlluminantD50D10[3];
extern const double kIlluminantD55[3];
extern const double kIlluminantD55D10[3];
extern const double kIlluminantD65[3];
extern const double kIlluminantD65D10[3];
extern const double kIlluminantD75[3];
extern const double kIlluminantD75D10[3];
extern const double kIlluminantF2[3];
extern const double kIlluminantF2D10[3];
extern const double kIlluminantF7[3];
extern const double kIlluminantF7D10[3];
extern const double kIlluminantF11[3];
extern const double kIlluminantF11D10[3];
void *GetChromaticAdaptationMatrix(double[3][3], const double[3],
const double[3]);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_ILLUMINANT_H_ */

46
dsp/core/inv3.c Normal file
View file

@ -0,0 +1,46 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#include "libc/math.h"
#include "libc/str/str.h"
/**
* Computes 𝐀¹ inverted 3×3 matrix, if it exists.
*
* @param 𝐁 is destination memory
* @param 𝐀 is input matrix, which can't overlap 𝐁
* @param 𝑑 is |𝐀| the determinant scalar or 0 if degenerate
* @return 𝐀¹ stored inside 𝐁 or NaNs if 𝑑=0
* @define 𝐀¹=𝐁 such that 𝐀×𝐁=𝐁×𝐀=𝐈
* @see det3()
*/
void *inv3(double B[restrict 3][3], const double A[restrict 3][3], double d) {
d = d ? 1 / d : NAN;
B[0][0] = (A[1][1] * A[2][2] - A[2][1] * A[1][2]) * d;
B[0][1] = (A[2][1] * A[0][2] - A[0][1] * A[2][2]) * d;
B[0][2] = (A[0][1] * A[1][2] - A[1][1] * A[0][2]) * d;
B[1][0] = (A[2][0] * A[1][2] - A[1][0] * A[2][2]) * d;
B[1][1] = (A[0][0] * A[2][2] - A[2][0] * A[0][2]) * d;
B[1][2] = (A[1][0] * A[0][2] - A[0][0] * A[1][2]) * d;
B[2][0] = (A[1][0] * A[2][1] - A[2][0] * A[1][1]) * d;
B[2][1] = (A[2][0] * A[0][1] - A[0][0] * A[2][1]) * d;
B[2][2] = (A[0][0] * A[1][1] - A[1][0] * A[0][1]) * d;
return B;
}

23
dsp/core/ituround.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef COSMOPOLITAN_DSP_CORE_ITUROUND_H_
#define COSMOPOLITAN_DSP_CORE_ITUROUND_H_
#include "libc/math.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* An ITU recommended rounding function.
*
* 1. Negative numbers round toward zero
* 2. Positive numbers round toward infinity
*
* @see round(), rint()
*/
static inline pureconst artificial long ituround(double x) {
return floor(x + .5);
}
static inline pureconst artificial int ituroundf(float x) {
return floorf(x + .5f);
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_ITUROUND_H_ */

47
dsp/core/ks8.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef COSMOPOLITAN_DSP_CORE_KS8_H_
#define COSMOPOLITAN_DSP_CORE_KS8_H_
#include "libc/macros.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* Performs 16-bit scaled rounded madd w/ eight coefficients or fewer.
*
* (Σ𝑘𝑥 + 2¹)/2
*
* @note intent is avoiding type promotion
*/
#define KS8(M, K1, K2, K3, K4, K5, K6, K7, K8, X1, X2, X3, X4, X5, X6, X7, X8) \
({ \
short x1, x2, x3, x4, x5, x6, x7, x8; \
x1 = X1; \
x2 = X2; \
x3 = X3; \
x4 = X4; \
x5 = X5; \
x6 = X6; \
x7 = X7; \
x8 = X8; \
x1 *= K1; \
x2 *= K2; \
x3 *= K3; \
x4 *= K4; \
x5 *= K5; \
x6 *= K6; \
x7 *= K7; \
x8 *= K8; \
x1 += x2; \
x3 += x4; \
x5 += x6; \
x7 += x8; \
x1 += x3; \
x5 += x7; \
x1 += x5; \
if (M) { \
x1 += 1 << MAX(0, M - 1); \
x1 >>= M; \
} \
x1; \
})
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_KS8_H_ */

43
dsp/core/kss8.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef COSMOPOLITAN_DSP_CORE_KSS8_H_
#define COSMOPOLITAN_DSP_CORE_KSS8_H_
#include "libc/limits.h"
#include "libc/macros.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* Performs 16-bit scaled rounded saturated madd w/ eight coefficients or fewer.
*
* (Σ𝑘𝑥 + 2¹)/2
*
* @note compiler struggles with this
*/
#define KSS8(M, K1, K2, K3, K4, K5, K6, K7, K8, X1, X2, X3, X4, X5, X6, X7, \
X8) \
({ \
short x1, x2, x3, x4, x5, x6, x7, x8; \
x1 = X1, x2 = X2, x3 = X3, x4 = X4; \
x5 = X5, x6 = X6, x7 = X7, x8 = X8; \
x1 = MIN(SHRT_MAX, MAX(SHRT_MIN, x1 * K1)); \
x2 = MIN(SHRT_MAX, MAX(SHRT_MIN, x2 * K2)); \
x3 = MIN(SHRT_MAX, MAX(SHRT_MIN, x3 * K3)); \
x4 = MIN(SHRT_MAX, MAX(SHRT_MIN, x4 * K4)); \
x5 = MIN(SHRT_MAX, MAX(SHRT_MIN, x5 * K5)); \
x6 = MIN(SHRT_MAX, MAX(SHRT_MIN, x6 * K6)); \
x7 = MIN(SHRT_MAX, MAX(SHRT_MIN, x7 * K7)); \
x8 = MIN(SHRT_MAX, MAX(SHRT_MIN, x8 * K8)); \
x1 = MIN(SHRT_MAX, MAX(SHRT_MIN, x1 + x2)); \
x3 = MIN(SHRT_MAX, MAX(SHRT_MIN, x3 + x4)); \
x5 = MIN(SHRT_MAX, MAX(SHRT_MIN, x5 + x6)); \
x7 = MIN(SHRT_MAX, MAX(SHRT_MIN, x7 + x8)); \
x1 = MIN(SHRT_MAX, MAX(SHRT_MIN, x1 + x3)); \
x5 = MIN(SHRT_MAX, MAX(SHRT_MIN, x5 + x7)); \
x1 = MIN(SHRT_MAX, MAX(SHRT_MIN, x1 + x5)); \
if (M) { \
x1 += 1 << MAX(0, M - 1); \
x1 >>= M; \
} \
x1; \
})
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_KSS8_H_ */

38
dsp/core/matmul3.c Normal file
View file

@ -0,0 +1,38 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#include "libc/str/str.h"
/**
* Multiplies 3×3 matrices.
*/
void *matmul3(double R[restrict 3][3], const double A[3][3],
const double B[3][3]) {
int i, j, k;
memset(R, 0, sizeof(double) * 3 * 3);
for (i = 0; i < 3; ++i) {
for (j = 0; j < 3; ++j) {
for (k = 0; k < 3; ++k) {
R[i][j] += A[k][j] * B[i][k];
}
}
}
return R;
}

32
dsp/core/matvmul3.c Normal file
View file

@ -0,0 +1,32 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
/**
* Computes M×V.
*
* @see vmatmul3() for noncommutative corollary
*/
void *matvmul3(double R[restrict 3], const double M[3][3], const double V[3]) {
R[0] = V[0] * M[0][0] + V[1] * M[0][1] + V[2] * M[0][2];
R[1] = V[0] * M[1][0] + V[1] * M[1][1] + V[2] * M[1][2];
R[2] = V[0] * M[2][0] + V[1] * M[2][1] + V[2] * M[2][2];
return R;
}

64
dsp/core/mulaw.S Normal file
View file

@ -0,0 +1,64 @@
/*-*- mode:asm; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify │
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License. │
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of │
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software │
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/macros.h"
#define BIAS 0x84
/ Encodes audio sample with µ-Law.
/
/ This is both the highest quality and most widely supported
/ telephony codec, whose use was phased out in the 2000's in
/ favor of cost-saving GSM audio compression that was so bad
/ consumers were willing to pay more cash, for the privilege
/ of saving telcos even more money w/ text messaging. Mu Law
/ reduces PCM data to half its original size, by diminishing
/ audio bands not vocalized by human voice.
/
/ @param %edi is pcm sample
/ @return %eax is uint8_t encoded sample
mulaw: .leafprologue
.profilable
mov $BIAS,%eax
xor %edx,%edx
test %edi,%edi
js 1f
lea (%edi,%eax),%eax
mov $0xFF,%dl
jmp 2f
1: sub %edi,%eax
mov $0x7F,%dl
2: mov %eax,%esi
or $0xFF,%sil
bsr %esi,%esi
sub $7,%esi
cmp $8,%esi
jge 4f
lea 3(%rdx),%ecx
sar %cl,%eax
and $0xF,%eax
shl $4,%esi
or %esi,%eax
xor %edx,%eax
3: .leafepilogue
4: xor $0x7F,%dl
mov %edx,%eax
jmp 3b
.endfn mulaw,globl
.yoink __FILE__

27
dsp/core/q.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef COSMOPOLITAN_DSP_CORE_Q_H_
#define COSMOPOLITAN_DSP_CORE_Q_H_
#include "libc/limits.h"
#include "libc/macros.h"
#include "libc/math.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* @fileoverview Fixed point arithmetic macros.
* @see en.wikipedia.org/wiki/Q_(number_format)
*/
#define F2Q(Q, I) MIN((1 << Q) - 1, roundf((I) * (1.f * ((1 << Q) - 1))))
#define Q2F(Q, I) ((I) * (1.f / ((1 << Q) - 1)))
#define QRS(Q, X) (((X) + (1 << (Q - 1))) >> Q)
#define LQRS(Q, X) (((X) + (1L << (Q - 1))) >> Q)
double DifferSumSq(const double[static 6], double, double);
double DifferSumSq8(const double[static 8], double, double);
long GetIntegerCoefficients(long[static 6], const double[static 6], long, long,
long);
long GetIntegerCoefficients8(long[static 8], const double[static 8], long, long,
long);
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_Q_H_ */

41
dsp/core/sad16x8n.S Normal file
View file

@ -0,0 +1,41 @@
/*-*- mode:asm; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify │
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License. │
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of │
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software │
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/macros.h"
.align 16
/ Mixes audio.
/
/ @param rdi is # aligned int16[16] sample chunks to process
/ @param rsi points to aligned pcm s16le input/output memory
/ @param rdx points to aligned pcm s16le [0..1] input memory
sad16x8n:
.leafprologue
.profilable
test %rdi,%rdi
jz 1f
shl $3,%rdi
0: sub $8,%rdi
movdqa (%rsi,%rdi,2),%xmm0
paddsw (%rdx,%rdi,2),%xmm0
movdqa %xmm0,(%rsi,%rdi,2)
jnz 0b
1: .leafepilogue
.endfn sad16x8n,globl,hidden
.yoink __FILE__

50
dsp/core/scalevolume.c Normal file
View file

@ -0,0 +1,50 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/limits.h"
/**
* Increases or decreases audio volume.
*
* @param 𝑝 is two-power w/ effective range [-15,15]
*/
void scalevolume(size_t n, int16_t pcm[n][8], int p) {
/* TODO(jart): This isn't acceptable. */
size_t i, j;
if (p > 0) {
if (p > 15) p = 15;
for (i = 0; i < n; ++i) {
for (j = 0; j < 8; ++j) {
pcm[i][j] =
MIN(SHRT_MAX, MAX(SHRT_MIN, (int)((unsigned)pcm[i][j] << p)));
}
}
} else if (p < 0) {
p = -p;
if (p > 15) p = 15;
for (i = 0; i < n; ++i) {
for (j = 0; j < 8; ++j) {
pcm[i][j] = SAR(pcm[i][j], p);
}
}
}
}

24
dsp/core/twixt8.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef COSMOPOLITAN_DSP_CORE_TWIXT8_H_
#define COSMOPOLITAN_DSP_CORE_TWIXT8_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* 8-bit linear interpolation kernel.
*/
static inline pureconst artificial unsigned char twixt8(unsigned char al,
unsigned char bl,
unsigned char p) {
short bx;
bx = bl;
bx -= al;
bx *= p;
bx >>= 8;
bx += al;
al = bx;
return al;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_CORE_TWIXT8_H_ */

32
dsp/core/vmatmul3.c Normal file
View file

@ -0,0 +1,32 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
/**
* Computes V×M.
*
* @see matvmul3() for noncommutative corollary
*/
void *vmatmul3(double R[restrict 3], const double V[3], const double M[3][3]) {
R[0] = V[0] * M[0][0] + V[1] * M[1][0] + V[2] * M[2][0];
R[1] = V[0] * M[0][1] + V[1] * M[1][1] + V[2] * M[2][1];
R[2] = V[0] * M[0][2] + V[1] * M[1][2] + V[2] * M[2][2];
return R;
}

8
dsp/dsp.mk Normal file
View file

@ -0,0 +1,8 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
.PHONY: o/$(MODE)/dsp
o/$(MODE)/dsp: o/$(MODE)/dsp/core \
o/$(MODE)/dsp/mpeg \
o/$(MODE)/dsp/scale \
o/$(MODE)/dsp/tty

92
dsp/mpeg/README.txt Normal file
View file

@ -0,0 +1,92 @@
PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
Dominic Szablewski - https://phoboslab.org
-- Synopsis
// This function gets called for each decoded video frame
void my_video_callback(plm_t *plm, plm_frame_t *frame, void *user) {
// Do something with frame->y.data, frame->cr.data, frame->cb.data
}
// This function gets called for each decoded audio frame
void my_audio_callback(plm_t *plm, plm_samples_t *frame, void *user) {
// Do something with samples->interleaved
}
// Load a .mpg (MPEG Program Stream) file
plm_t *plm = plm_create_with_filename("some-file.mpg");
// Install the video & audio decode callbacks
plm_set_video_decode_callback(plm, my_video_callback, my_data);
plm_set_audio_decode_callback(plm, my_audio_callback, my_data);
// Decode
do {
plm_decode(plm, time_since_last_call);
} while (!plm_has_ended(plm));
// All done
plm_destroy(plm);
-- Documentation
This library provides several interfaces to load, demux and decode MPEG video
and audio data. A high-level API combines the demuxer, video & audio decoders
in an easy to use wrapper.
Lower-level APIs for accessing the demuxer, video decoder and audio decoder,
as well as providing different data sources are also available.
Interfaces are written in an object orientet style, meaning you create object
instances via various different constructor functions (plm_*create()),
do some work on them and later dispose them via plm_*destroy().
plm_* -- the high-level interface, combining demuxer and decoders
plm_buffer_* -- the data source used by all interfaces
plm_demux_* -- the MPEG-PS demuxer
plm_video_* -- the MPEG1 Video ("mpeg1") decoder
plm_audio_* -- the MPEG1 Audio Layer II ("mp2") decoder
This library uses malloc(), realloc() and free() to manage memory. Typically
all allocation happens up-front when creating the interface. However, the
default buffer size may be too small for certain inputs. In these cases plmpeg
will realloc() the buffer with a larger size whenever needed. You can configure
the default buffer size by defining PLM_BUFFER_DEFAULT_SIZE *before*
including this library.
With the high-level interface you have two options to decode video & audio:
1) Use plm_decode() and just hand over the delta time since the last call.
It will decode everything needed and call your callbacks (specified through
plm_set_{video|audio}_decode_callback()) any number of times.
2) Use plm_decode_video() and plm_decode_audio() to decode exactly one
frame of video or audio data at a time. How you handle the synchronization of
both streams is up to you.
If you only want to decode video *or* audio through these functions, you should
disable the other stream (plm_set_{video|audio}_enabled(false))
Video data is decoded into a struct with all 3 planes (Y, Cr, Cb) stored in
separate buffers. You can either convert this to RGB on the CPU (slow) via the
plm_frame_to_rgb() function or do it on the GPU with the following matrix:
mat4 rec601 = mat4(
1.16438, 0.00000, 1.59603, -0.87079,
1.16438, -0.39176, -0.81297, 0.52959,
1.16438, 2.01723, 0.00000, -1.08139,
0, 0, 0, 1
);
gl_FragColor = vec4(y, cb, cr, 1.0) * rec601;
Audio data is decoded into a struct with either one single float array with the
samples for the left and right channel interleaved, or if the
PLM_AUDIO_SEPARATE_CHANNELS is defined *before* including this library, into
two separate float arrays - one for each channel.
See below for detailed the API documentation.

22
dsp/mpeg/blockset.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef COSMOPOLITAN_DSP_MPEG_BLOCKSET_H_
#define COSMOPOLITAN_DSP_MPEG_BLOCKSET_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#define PLM_BLOCK_SET(DEST, DEST_INDEX, DEST_WIDTH, SOURCE_INDEX, \
SOURCE_WIDTH, BLOCK_SIZE, OP) \
do { \
int dest_scan = DEST_WIDTH - BLOCK_SIZE; \
int source_scan = SOURCE_WIDTH - BLOCK_SIZE; \
for (int y = 0; y < BLOCK_SIZE; y++) { \
for (int x = 0; x < BLOCK_SIZE; x++) { \
DEST[DEST_INDEX] = OP; \
SOURCE_INDEX++; \
DEST_INDEX++; \
} \
SOURCE_INDEX += source_scan; \
DEST_INDEX += dest_scan; \
} \
} while (false)
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_MPEG_BLOCKSET_H_ */

158
dsp/mpeg/buffer.c Normal file
View file

@ -0,0 +1,158 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
vi: set et ft=c ts=4 sw=4 fenc=utf-8 :vi
PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
Dominic Szablewski - https://phoboslab.org │
The MIT License(MIT)
Copyright(c) 2019 Dominic Szablewski
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 "dsp/mpeg/buffer.h"
#include "dsp/mpeg/mpeg.h"
#include "libc/calls/calls.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/madv.h"
asm(".ident\t\"\\n\\n\
PL_MPEG (MIT License)\\n\
Copyright(c) 2019 Dominic Szablewski\\n\
https://phoboslab.org\"");
asm(".include \"libc/disclaimer.inc\"");
/* clang-format off */
// -----------------------------------------------------------------------------
// plm_buffer implementation
plm_buffer_t *plm_buffer_create_with_filename(const char *filename) {
FILE *fh = fopen(filename, "rb");
if (!fh) {
return NULL;
}
fadvise(fileno(fh), 0, 0, MADV_SEQUENTIAL);
return plm_buffer_create_with_file(fh, true);
}
plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done) {
plm_buffer_t *b;
b = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
b->fh = fh;
b->close_when_done = close_when_done;
b->mode = PLM_BUFFER_MODE_FILE;
plm_buffer_set_load_callback(b, plm_buffer_load_file_callback, NULL);
return b;
}
plm_buffer_t *plm_buffer_create_with_memory(unsigned char *bytes, size_t length, int free_when_done) {
plm_buffer_t *b;
b = memalign(alignof(plm_buffer_t), sizeof(plm_buffer_t));
memset(b, 0, sizeof(plm_buffer_t));
b->capacity = length;
b->length = length;
b->free_when_done = free_when_done;
b->bytes = bytes;
b->mode = PLM_BUFFER_MODE_FIXED_MEM;
return b;
}
plm_buffer_t * plm_buffer_create_with_capacity(size_t capacity) {
plm_buffer_t *b;
b = memalign(alignof(plm_buffer_t), sizeof(plm_buffer_t));
memset(b, 0, sizeof(plm_buffer_t));
b->capacity = capacity;
b->free_when_done = true;
b->bytes = (unsigned char *)malloc(capacity);
b->mode = PLM_BUFFER_MODE_DYNAMIC_MEM;
return b;
}
void plm_buffer_destroy(plm_buffer_t *self) {
if (self->fh && self->close_when_done) {
fclose(self->fh);
}
if (self->free_when_done) {
free(self->bytes);
}
free(self);
}
size_t plm_buffer_write(plm_buffer_t *self, unsigned char *bytes, size_t length) {
if (self->mode == PLM_BUFFER_MODE_FIXED_MEM) {
return 0;
}
// This should be a ring buffer, but instead it just shifts all unread data
// to the beginning of the buffer and appends new data at the end. Seems
// to be good enough.
plm_buffer_discard_read_bytes(self);
// Do we have to resize to fit the new data?
size_t bytes_available = self->capacity - self->length;
if (bytes_available < length) {
size_t new_size = self->capacity;
do {
new_size *= 2;
} while (new_size - self->length < length);
self->bytes = (unsigned char *)realloc(self->bytes, new_size);
self->capacity = new_size;
}
memcpy(self->bytes + self->length, bytes, length);
self->length += length;
return length;
}
void plm_buffer_set_load_callback(plm_buffer_t *self, plm_buffer_load_callback fp, void *user) {
self->load_callback = fp;
self->load_callback_user_data = user;
}
void plm_buffer_rewind(plm_buffer_t *self) {
if (self->fh) {
fseek(self->fh, 0, SEEK_SET);
self->length = 0;
}
if (self->mode != PLM_BUFFER_MODE_FIXED_MEM) {
self->length = 0;
}
self->bit_index = 0;
}
void plm_buffer_discard_read_bytes(plm_buffer_t *self) {
size_t byte_pos = self->bit_index >> 3;
if (byte_pos == self->length) {
self->bit_index = 0;
self->length = 0;
}
else if (byte_pos > 0) {
memmove(self->bytes, self->bytes + byte_pos, self->length - byte_pos);
self->bit_index -= byte_pos << 3;
self->length -= byte_pos;
}
}
void plm_buffer_load_file_callback(plm_buffer_t *self, void *user) {
plm_buffer_discard_read_bytes(self);
unsigned bytes_available = self->capacity - self->length;
unsigned bytes_read = fread(self->bytes + self->length, 1, bytes_available, self->fh);
self->length += bytes_read;
}

163
dsp/mpeg/buffer.h Normal file
View file

@ -0,0 +1,163 @@
#ifndef COSMOPOLITAN_DSP_MPEG_BUFFER_H_
#define COSMOPOLITAN_DSP_MPEG_BUFFER_H_
#include "dsp/mpeg/mpeg.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct FILE;
enum plm_buffer_mode {
PLM_BUFFER_MODE_FILE,
PLM_BUFFER_MODE_FIXED_MEM,
PLM_BUFFER_MODE_DYNAMIC_MEM
};
typedef struct plm_buffer_t {
unsigned bit_index;
unsigned capacity;
unsigned length;
int free_when_done;
int close_when_done;
struct FILE *fh;
plm_buffer_load_callback load_callback;
void *load_callback_user_data;
unsigned char *bytes;
enum plm_buffer_mode mode;
} plm_buffer_t;
typedef struct {
int16_t index;
int16_t value;
} plm_vlc_t;
typedef struct {
int16_t index;
uint16_t value;
} plm_vlc_uint_t;
/* bool plm_buffer_has(plm_buffer_t *, size_t); */
/* int plm_buffer_read(plm_buffer_t *, int); */
/* void plm_buffer_align(plm_buffer_t *); */
/* void plm_buffer_skip(plm_buffer_t *, size_t); */
/* int plm_buffer_skip_bytes(plm_buffer_t *, unsigned char); */
/* int plm_buffer_next_start_code(plm_buffer_t *); */
/* int plm_buffer_find_start_code(plm_buffer_t *, int); */
/* int plm_buffer_no_start_code(plm_buffer_t *); */
/* int16_t plm_buffer_read_vlc(plm_buffer_t *, const plm_vlc_t *); */
/* uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *, const plm_vlc_uint_t *); */
void plm_buffer_discard_read_bytes(plm_buffer_t *);
relegated void plm_buffer_load_file_callback(plm_buffer_t *, void *);
forceinline bool plm_buffer_has(plm_buffer_t *b, size_t bits) {
unsigned have;
have = b->length;
have <<= 3;
have -= b->bit_index;
if (bits <= have) {
return true;
} else {
if (b->load_callback) {
b->load_callback(b, b->load_callback_user_data);
return ((b->length << 3) - b->bit_index) >= bits;
} else {
return false;
}
}
}
forceinline int plm_buffer_read(plm_buffer_t *self, int count) {
if (!plm_buffer_has(self, count)) return 0;
int value = 0;
while (count) {
int current_byte = self->bytes[self->bit_index >> 3];
int remaining = 8 - (self->bit_index & 7); // Remaining bits in byte
int read = remaining < count ? remaining : count; // Bits in self run
int shift = remaining - read;
int mask = (0xff >> (8 - read));
value = (value << read) | ((current_byte & (mask << shift)) >> shift);
self->bit_index += read;
count -= read;
}
return value;
}
forceinline void plm_buffer_align(plm_buffer_t *self) {
self->bit_index = ((self->bit_index + 7) >> 3) << 3;
}
forceinline void plm_buffer_skip(plm_buffer_t *self, size_t count) {
if (plm_buffer_has(self, count)) {
self->bit_index += count;
}
}
forceinline int plm_buffer_skip_bytes(plm_buffer_t *self, unsigned char v) {
unsigned skipped;
plm_buffer_align(self);
skipped = 0;
while (plm_buffer_has(self, 8)) {
if (v == self->bytes[self->bit_index >> 3]) {
self->bit_index += 8;
++skipped;
} else {
break;
}
}
return skipped;
}
forceinline int plm_buffer_next_start_code(plm_buffer_t *self) {
plm_buffer_align(self);
while (plm_buffer_has(self, (5 << 3))) {
size_t byte_index = (self->bit_index) >> 3;
if (self->bytes[byte_index] == 0x00 &&
self->bytes[byte_index + 1] == 0x00 &&
self->bytes[byte_index + 2] == 0x01) {
self->bit_index = (byte_index + 4) << 3;
return self->bytes[byte_index + 3];
}
self->bit_index += 8;
}
self->bit_index = (self->length << 3);
return -1;
}
forceinline int plm_buffer_find_start_code(plm_buffer_t *self, int code) {
int current = 0;
while (true) {
current = plm_buffer_next_start_code(self);
if (current == code || current == -1) {
return current;
}
}
return -1;
}
forceinline int plm_buffer_no_start_code(plm_buffer_t *self) {
if (!plm_buffer_has(self, (5 << 3))) {
return false;
}
size_t byte_index = ((self->bit_index + 7) >> 3);
return !(self->bytes[byte_index] == 0x00 &&
self->bytes[byte_index + 1] == 0x00 &&
self->bytes[byte_index + 2] == 0x01);
}
forceinline int16_t plm_buffer_read_vlc(plm_buffer_t *self,
const plm_vlc_t *table) {
plm_vlc_t state = {0, 0};
do {
state = table[state.index + plm_buffer_read(self, 1)];
} while (state.index > 0);
return state.value;
}
forceinline uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *self,
const plm_vlc_uint_t *table) {
return (uint16_t)plm_buffer_read_vlc(self, (plm_vlc_t *)table);
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_MPEG_BUFFER_H_ */

View file

@ -0,0 +1,32 @@
/*-*- mode:asm; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify │
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License. │
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of │
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software │
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/macros.h"
.yoink __FILE__
clamp4int256$core:
.leafprologue
pxor %xmm1,%xmm1
pmaxsd %xmm1,%xmm0
pminsd 0f(%rip),%xmm0
.leafepilogue
.endfn clamp4int256$core,globl
.rodata.cst16
0: .long 255,255,255,255

208
dsp/mpeg/demux.c Normal file
View file

@ -0,0 +1,208 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
vi: set et ft=c ts=4 sw=4 fenc=utf-8 :vi
PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
Dominic Szablewski - https://phoboslab.org │
The MIT License(MIT)
Copyright(c) 2019 Dominic Szablewski
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 "dsp/mpeg/buffer.h"
#include "dsp/mpeg/demux.h"
#include "dsp/mpeg/mpeg.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
asm(".ident\t\"\\n\\n\
PL_MPEG (MIT License)\\n\
Copyright(c) 2019 Dominic Szablewski\\n\
https://phoboslab.org\"");
asm(".include \"libc/disclaimer.inc\"");
/* clang-format off */
// ----------------------------------------------------------------------------
// plm_demux implementation
plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done) {
plm_demux_t *self = (plm_demux_t *)malloc(sizeof(plm_demux_t));
memset(self, 0, sizeof(plm_demux_t));
self->buffer = buffer;
self->destroy_buffer_when_done = destroy_when_done;
if (plm_buffer_find_start_code(self->buffer, START_PACK) != -1) {
plm_demux_decode_pack_header(self);
}
if (plm_buffer_find_start_code(self->buffer, START_SYSTEM) != -1) {
plm_demux_decode_system_header(self);
}
return self;
}
void plm_demux_destroy(plm_demux_t *self) {
if (self->destroy_buffer_when_done) {
plm_buffer_destroy(self->buffer);
}
free(self);
}
int plm_demux_get_num_video_streams(plm_demux_t *self) {
return self->num_video_streams;
}
int plm_demux_get_num_audio_streams(plm_demux_t *self) {
return self->num_audio_streams;
}
void plm_demux_rewind(plm_demux_t *self) {
plm_buffer_rewind(self->buffer);
}
plm_packet_t *plm_demux_decode(plm_demux_t *self) {
if (self->current_packet.length) {
size_t bits_till_next_packet = self->current_packet.length << 3;
if (!plm_buffer_has(self->buffer, bits_till_next_packet)) {
return NULL;
}
plm_buffer_skip(self->buffer, bits_till_next_packet);
self->current_packet.length = 0;
}
if (!self->has_pack_header) {
if (plm_buffer_find_start_code(self->buffer, START_PACK) != -1) {
plm_demux_decode_pack_header(self);
}
else {
return NULL;
}
}
if (!self->has_system_header) {
if (plm_buffer_find_start_code(self->buffer, START_SYSTEM) != -1) {
plm_demux_decode_system_header(self);
}
else {
return NULL;
}
}
// pending packet just waiting for data?
if (self->next_packet.length) {
return plm_demux_get_packet(self);
}
int code;
do {
code = plm_buffer_next_start_code(self->buffer);
if (
code == PLM_DEMUX_PACKET_VIDEO_1 ||
code == PLM_DEMUX_PACKET_PRIVATE ||
(code >= PLM_DEMUX_PACKET_AUDIO_1 && code <= PLM_DEMUX_PACKET_AUDIO_4)
) {
return plm_demux_decode_packet(self, code);
}
} while (code != -1);
return NULL;
}
double plm_demux_read_time(plm_demux_t *self) {
int64_t clock = plm_buffer_read(self->buffer, 3) << 30;
plm_buffer_skip(self->buffer, 1);
clock |= plm_buffer_read(self->buffer, 15) << 15;
plm_buffer_skip(self->buffer, 1);
clock |= plm_buffer_read(self->buffer, 15);
plm_buffer_skip(self->buffer, 1);
return (double)clock / 90000.0;
}
void plm_demux_decode_pack_header(plm_demux_t *self) {
if (plm_buffer_read(self->buffer, 4) != 0x02) {
return; // invalid
}
self->system_clock_ref = plm_demux_read_time(self);
plm_buffer_skip(self->buffer, 1);
plm_buffer_skip(self->buffer, 22); // mux_rate * 50
plm_buffer_skip(self->buffer, 1);
self->has_pack_header = true;
}
void plm_demux_decode_system_header(plm_demux_t *self) {
plm_buffer_skip(self->buffer, 16); // header_length
plm_buffer_skip(self->buffer, 24); // rate bound
self->num_audio_streams = plm_buffer_read(self->buffer, 6);
plm_buffer_skip(self->buffer, 5); // misc flags
self->num_video_streams = plm_buffer_read(self->buffer, 5);
self->has_system_header = true;
}
plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int start_code) {
if (!plm_buffer_has(self->buffer, 8 << 3)) {
return NULL;
}
self->next_packet.type = start_code;
self->next_packet.length = plm_buffer_read(self->buffer, 16);
self->next_packet.length -= plm_buffer_skip_bytes(self->buffer, 0xff); // stuffing
// skip P-STD
if (plm_buffer_read(self->buffer, 2) == 0x01) {
plm_buffer_skip(self->buffer, 16);
self->next_packet.length -= 2;
}
int pts_dts_marker = plm_buffer_read(self->buffer, 2);
if (pts_dts_marker == 0x03) {
self->next_packet.pts = plm_demux_read_time(self);
plm_buffer_skip(self->buffer, 40); // skip dts
self->next_packet.length -= 10;
}
else if (pts_dts_marker == 0x02) {
self->next_packet.pts = plm_demux_read_time(self);
self->next_packet.length -= 5;
}
else if (pts_dts_marker == 0x00) {
self->next_packet.pts = 0;
plm_buffer_skip(self->buffer, 4);
self->next_packet.length -= 1;
}
else {
return NULL; // invalid
}
return plm_demux_get_packet(self);
}
plm_packet_t *plm_demux_get_packet(plm_demux_t *self) {
if (!plm_buffer_has(self->buffer, self->next_packet.length << 3)) {
return NULL;
}
self->current_packet.data = self->buffer->bytes + (self->buffer->bit_index >> 3);
self->current_packet.length = self->next_packet.length;
self->current_packet.type = self->next_packet.type;
self->current_packet.pts = self->next_packet.pts;
self->next_packet.length = 0;
return &self->current_packet;
}

31
dsp/mpeg/demux.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef COSMOPOLITAN_DSP_MPEG_DEMUX_H_
#define COSMOPOLITAN_DSP_MPEG_DEMUX_H_
#include "dsp/mpeg/mpeg.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define START_PACK 0xBA
#define START_END 0xB9
#define START_SYSTEM 0xBB
typedef struct plm_demux_t {
plm_buffer_t *buffer;
int destroy_buffer_when_done;
double system_clock_ref;
int has_pack_header;
int has_system_header;
int num_audio_streams;
int num_video_streams;
plm_packet_t current_packet;
plm_packet_t next_packet;
} plm_demux_t;
double plm_demux_read_time(plm_demux_t *self);
void plm_demux_decode_pack_header(plm_demux_t *self);
void plm_demux_decode_system_header(plm_demux_t *self);
plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int start_code);
plm_packet_t *plm_demux_get_packet(plm_demux_t *self);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_MPEG_DEMUX_H_ */

106
dsp/mpeg/idct.c Normal file
View file

@ -0,0 +1,106 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
vi: set et ft=c ts=4 sw=4 fenc=utf-8 :vi
PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
Dominic Szablewski - https://phoboslab.org │
The MIT License(MIT)
Copyright(c) 2019 Dominic Szablewski
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 "dsp/core/half.h"
asm(".ident\t\"\\n\\n\
PL_MPEG (MIT License)\\n\
Copyright(c) 2019 Dominic Szablewski\\n\
https://phoboslab.org\"");
asm(".include \"libc/disclaimer.inc\"");
/**
* Computes Fixed-Point 8x8 Inverse Discrete Cosine Transform.
*
* @note discovered by Nasir Ahmed
*/
void plm_video_idct(int block[8][8]) {
int i, j, t1, t2, m0;
int b1, b3, b4, b6, b7;
int y3, y4, y5, y6, y7;
int x0, x1, x2, x3, x4;
for (i = 0; i < 8; ++i) {
b1 = block[4][i];
b3 = block[2][i] + block[6][i];
b4 = block[5][i] - block[3][i];
t1 = block[1][i] + block[7][i];
t2 = block[3][i] + block[5][i];
b6 = block[1][i] - block[7][i];
b7 = t1 + t2;
m0 = block[0][i];
x4 = ((b6 * 473 - b4 * 196 + 128) >> 8) - b7;
x0 = x4 - (((t1 - t2) * 362 + 128) >> 8);
x1 = m0 - b1;
x2 = (((block[2][i] - block[6][i]) * 362 + 128) >> 8) - b3;
x3 = m0 + b1;
y3 = x1 + x2;
y4 = x3 + b3;
y5 = x1 - x2;
y6 = x3 - b3;
y7 = -x0 - ((b4 * 473 + b6 * 196 + 128) >> 8);
block[0][i] = b7 + y4;
block[1][i] = x4 + y3;
block[2][i] = y5 - x0;
block[3][i] = y6 - y7;
block[4][i] = y6 + y7;
block[5][i] = x0 + y5;
block[6][i] = y3 - x4;
block[7][i] = y4 - b7;
}
for (i = 0; i < 8; ++i) {
b1 = block[i][4];
b3 = block[i][2] + block[i][6];
b4 = block[i][5] - block[i][3];
t1 = block[i][1] + block[i][7];
t2 = block[i][3] + block[i][5];
b6 = block[i][1] - block[i][7];
b7 = t1 + t2;
m0 = block[i][0];
x4 = ((b6 * 473 - b4 * 196 + 128) >> 8) - b7;
x0 = x4 - (((t1 - t2) * 362 + 128) >> 8);
x1 = m0 - b1;
x2 = (((block[i][2] - block[i][6]) * 362 + 128) >> 8) - b3;
x3 = m0 + b1;
y3 = x1 + x2;
y4 = x3 + b3;
y5 = x1 - x2;
y6 = x3 - b3;
y7 = -x0 - ((b4 * 473 + b6 * 196 + 128) >> 8);
block[i][0] = (b7 + y4 + 128) >> 8;
block[i][1] = (x4 + y3 + 128) >> 8;
block[i][2] = (y5 - x0 + 128) >> 8;
block[i][3] = (y6 - y7 + 128) >> 8;
block[i][4] = (y6 + y7 + 128) >> 8;
block[i][5] = (x0 + y5 + 128) >> 8;
block[i][6] = (y3 - x4 + 128) >> 8;
block[i][7] = (y4 - b7 + 128) >> 8;
}
}

10
dsp/mpeg/idct.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef COSMOPOLITAN_DSP_MPEG_IDCT_H_
#define COSMOPOLITAN_DSP_MPEG_IDCT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
void plm_video_idct(int *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_MPEG_IDCT_H_ */

171
dsp/mpeg/macroblock.c Normal file
View file

@ -0,0 +1,171 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
vi: set et ft=c ts=4 sw=4 fenc=utf-8 :vi
PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
Dominic Szablewski - https://phoboslab.org │
The MIT License(MIT)
Copyright(c) 2019 Dominic Szablewski
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 "dsp/mpeg/mpeg.h"
#include "dsp/mpeg/video.h"
#include "libc/log/check.h"
forceinline void plm_video_process_macroblock(plm_video_t *self,
uint8_t *restrict d,
uint8_t *restrict s, int motion_h,
int motion_v, bool interpolate,
unsigned BW) {
unsigned si, di, max_address;
int y, x, dest_scan, source_scan, dw, hp, vp, odd_h, odd_v;
dw = self->mb_width * BW;
hp = motion_h >> 1;
vp = motion_v >> 1;
odd_h = (motion_h & 1) == 1;
odd_v = (motion_v & 1) == 1;
si = ((self->mb_row * BW) + vp) * dw + (self->mb_col * BW) + hp;
di = (self->mb_row * dw + self->mb_col) * BW;
max_address = (dw * (self->mb_height * BW - BW + 1) - BW);
if (si > max_address || di > max_address) return;
d += di;
s += si;
switch (((interpolate << 2) | (odd_h << 1) | (odd_v)) & 7) {
case 0:
dest_scan = dw - BW;
source_scan = dw - BW;
for (y = 0; y < BW; y++) {
for (x = 0; x < BW; x++) {
*d++ = *s++;
}
s += source_scan;
d += dest_scan;
}
break;
case 1:
dest_scan = dw - BW;
source_scan = dw - BW;
for (y = 0; y < BW; y++) {
for (x = 0; x < BW; x++) {
*d++ = (s[0] + s[dw] + 1) >> 1;
s++;
}
s += source_scan;
d += dest_scan;
}
break;
case 2:
dest_scan = dw - BW;
source_scan = dw - BW;
for (y = 0; y < BW; y++) {
for (x = 0; x < BW; x++) {
*d++ = (s[0] + s[1] + 1) >> 1;
s++;
}
s += source_scan;
d += dest_scan;
}
break;
case 3:
dest_scan = dw - BW;
source_scan = dw - BW;
for (y = 0; y < BW; y++) {
for (x = 0; x < BW; x++) {
*d++ = (s[0] + s[1] + s[dw] + s[dw + 1] + 2) >> 2;
s++;
}
s += source_scan;
d += dest_scan;
}
break;
case 4:
dest_scan = dw - BW;
source_scan = dw - BW;
for (y = 0; y < BW; y++) {
for (x = 0; x < BW; x++) {
d[0] = (d[0] + (s[0]) + 1) >> 1;
d++;
s++;
}
s += source_scan;
d += dest_scan;
}
break;
case 5:
dest_scan = dw - BW;
source_scan = dw - BW;
for (y = 0; y < BW; y++) {
for (x = 0; x < BW; x++) {
d[0] = (d[0] + ((s[0] + s[dw] + 1) >> 1) + 1) >> 1;
d++;
s++;
}
s += source_scan;
d += dest_scan;
}
break;
case 6:
dest_scan = dw - BW;
source_scan = dw - BW;
for (y = 0; y < BW; y++) {
for (x = 0; x < BW; x++) {
d[0] = (d[0] + ((s[0] + s[1] + 1) >> 1) + 1) >> 1;
d++;
s++;
}
s += source_scan;
d += dest_scan;
}
break;
case 7:
dest_scan = dw - BW;
source_scan = dw - BW;
for (y = 0; y < BW; y++) {
for (x = 0; x < BW; x++) {
d[0] = (d[0] + ((s[0] + s[1] + s[dw] + s[dw + 1] + 2) >> 2) + 1) >> 1;
d++;
s++;
}
s += source_scan;
d += dest_scan;
}
break;
default:
break;
}
}
void plm_video_process_macroblock_8(plm_video_t *self, uint8_t *restrict d,
uint8_t *restrict s, int motion_h,
int motion_v, bool interpolate) {
DCHECK_ALIGNED(8, d);
DCHECK_ALIGNED(8, s);
plm_video_process_macroblock(self, d, s, motion_h, motion_v, interpolate, 8);
}
void plm_video_process_macroblock_16(plm_video_t *self, uint8_t *restrict d,
uint8_t *restrict s, int motion_h,
int motion_v, bool interpolate) {
DCHECK_ALIGNED(16, d);
DCHECK_ALIGNED(16, s);
plm_video_process_macroblock(self, d, s, motion_h, motion_v, interpolate, 16);
}

775
dsp/mpeg/mp2.c Normal file
View file

@ -0,0 +1,775 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
vi: set et ft=c ts=4 sw=4 fenc=utf-8 :vi
PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
Dominic Szablewski - https://phoboslab.org │
The MIT License(MIT)
Copyright(c) 2019 Dominic Szablewski
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 "dsp/mpeg/buffer.h"
#include "dsp/mpeg/mpeg.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
asm(".ident\t\"\\n\\n\
PL_MPEG (MIT License)\\n\
Copyright(c) 2019 Dominic Szablewski\\n\
https://phoboslab.org\"");
asm(".include \"libc/disclaimer.inc\"");
/* clang-format off */
// -----------------------------------------------------------------------------
// plm_audio implementation
// Based on kjmp2 by Martin J. Fiedler
// http://keyj.emphy.de/kjmp2/
#define PLM_AUDIO_FRAME_SYNC 0x7ff
#define PLM_AUDIO_MPEG_2_5 0x0
#define PLM_AUDIO_MPEG_2 0x2
#define PLM_AUDIO_MPEG_1 0x3
#define PLM_AUDIO_LAYER_III 0x1
#define PLM_AUDIO_LAYER_II 0x2
#define PLM_AUDIO_LAYER_I 0x3
#define PLM_AUDIO_MODE_STEREO 0x0
#define PLM_AUDIO_MODE_JOINT_STEREO 0x1
#define PLM_AUDIO_MODE_DUAL_CHANNEL 0x2
#define PLM_AUDIO_MODE_MONO 0x3
static const unsigned short PLM_AUDIO_SAMPLE_RATE[] = {
44100, 48000, 32000, 0, // MPEG-1
22050, 24000, 16000, 0 // MPEG-2
};
static const short PLM_AUDIO_BIT_RATE[] = {
32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, // MPEG-1
8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 // MPEG-2
};
static const int PLM_AUDIO_SCALEFACTOR_BASE[] = {
0x02000000, 0x01965FEA, 0x01428A30
};
static const float PLM_AUDIO_SYNTHESIS_WINDOW[] = {
0.0, -0.5, -0.5, -0.5, -0.5, -0.5,
-0.5, -1.0, -1.0, -1.0, -1.0, -1.5,
-1.5, -2.0, -2.0, -2.5, -2.5, -3.0,
-3.5, -3.5, -4.0, -4.5, -5.0, -5.5,
-6.5, -7.0, -8.0, -8.5, -9.5, -10.5,
-12.0, -13.0, -14.5, -15.5, -17.5, -19.0,
-20.5, -22.5, -24.5, -26.5, -29.0, -31.5,
-34.0, -36.5, -39.5, -42.5, -45.5, -48.5,
-52.0, -55.5, -58.5, -62.5, -66.0, -69.5,
-73.5, -77.0, -80.5, -84.5, -88.0, -91.5,
-95.0, -98.0, -101.0, -104.0, 106.5, 109.0,
111.0, 112.5, 113.5, 114.0, 114.0, 113.5,
112.0, 110.5, 107.5, 104.0, 100.0, 94.5,
88.5, 81.5, 73.0, 63.5, 53.0, 41.5,
28.5, 14.5, -1.0, -18.0, -36.0, -55.5,
-76.5, -98.5, -122.0, -147.0, -173.5, -200.5,
-229.5, -259.5, -290.5, -322.5, -355.5, -389.5,
-424.0, -459.5, -495.5, -532.0, -568.5, -605.0,
-641.5, -678.0, -714.0, -749.0, -783.5, -817.0,
-849.0, -879.5, -908.5, -935.0, -959.5, -981.0,
-1000.5, -1016.0, -1028.5, -1037.5, -1042.5, -1043.5,
-1040.0, -1031.5, 1018.5, 1000.0, 976.0, 946.5,
911.0, 869.5, 822.0, 767.5, 707.0, 640.0,
565.5, 485.0, 397.0, 302.5, 201.0, 92.5,
-22.5, -144.0, -272.5, -407.0, -547.5, -694.0,
-846.0, -1003.0, -1165.0, -1331.5, -1502.0, -1675.5,
-1852.5, -2031.5, -2212.5, -2394.0, -2576.5, -2758.5,
-2939.5, -3118.5, -3294.5, -3467.5, -3635.5, -3798.5,
-3955.0, -4104.5, -4245.5, -4377.5, -4499.0, -4609.5,
-4708.0, -4792.5, -4863.5, -4919.0, -4958.0, -4979.5,
-4983.0, -4967.5, -4931.5, -4875.0, -4796.0, -4694.5,
-4569.5, -4420.0, -4246.0, -4046.0, -3820.0, -3567.0,
3287.0, 2979.5, 2644.0, 2280.5, 1888.0, 1467.5,
1018.5, 541.0, 35.0, -499.0, -1061.0, -1650.0,
-2266.5, -2909.0, -3577.0, -4270.0, -4987.5, -5727.5,
-6490.0, -7274.0, -8077.5, -8899.5, -9739.0, -10594.5,
-11464.5, -12347.0, -13241.0, -14144.5, -15056.0, -15973.5,
-16895.5, -17820.0, -18744.5, -19668.0, -20588.0, -21503.0,
-22410.5, -23308.5, -24195.0, -25068.5, -25926.5, -26767.0,
-27589.0, -28389.0, -29166.5, -29919.0, -30644.5, -31342.0,
-32009.5, -32645.0, -33247.0, -33814.5, -34346.0, -34839.5,
-35295.0, -35710.0, -36084.5, -36417.5, -36707.5, -36954.0,
-37156.5, -37315.0, -37428.0, -37496.0, 37519.0, 37496.0,
37428.0, 37315.0, 37156.5, 36954.0, 36707.5, 36417.5,
36084.5, 35710.0, 35295.0, 34839.5, 34346.0, 33814.5,
33247.0, 32645.0, 32009.5, 31342.0, 30644.5, 29919.0,
29166.5, 28389.0, 27589.0, 26767.0, 25926.5, 25068.5,
24195.0, 23308.5, 22410.5, 21503.0, 20588.0, 19668.0,
18744.5, 17820.0, 16895.5, 15973.5, 15056.0, 14144.5,
13241.0, 12347.0, 11464.5, 10594.5, 9739.0, 8899.5,
8077.5, 7274.0, 6490.0, 5727.5, 4987.5, 4270.0,
3577.0, 2909.0, 2266.5, 1650.0, 1061.0, 499.0,
-35.0, -541.0, -1018.5, -1467.5, -1888.0, -2280.5,
-2644.0, -2979.5, 3287.0, 3567.0, 3820.0, 4046.0,
4246.0, 4420.0, 4569.5, 4694.5, 4796.0, 4875.0,
4931.5, 4967.5, 4983.0, 4979.5, 4958.0, 4919.0,
4863.5, 4792.5, 4708.0, 4609.5, 4499.0, 4377.5,
4245.5, 4104.5, 3955.0, 3798.5, 3635.5, 3467.5,
3294.5, 3118.5, 2939.5, 2758.5, 2576.5, 2394.0,
2212.5, 2031.5, 1852.5, 1675.5, 1502.0, 1331.5,
1165.0, 1003.0, 846.0, 694.0, 547.5, 407.0,
272.5, 144.0, 22.5, -92.5, -201.0, -302.5,
-397.0, -485.0, -565.5, -640.0, -707.0, -767.5,
-822.0, -869.5, -911.0, -946.5, -976.0, -1000.0,
1018.5, 1031.5, 1040.0, 1043.5, 1042.5, 1037.5,
1028.5, 1016.0, 1000.5, 981.0, 959.5, 935.0,
908.5, 879.5, 849.0, 817.0, 783.5, 749.0,
714.0, 678.0, 641.5, 605.0, 568.5, 532.0,
495.5, 459.5, 424.0, 389.5, 355.5, 322.5,
290.5, 259.5, 229.5, 200.5, 173.5, 147.0,
122.0, 98.5, 76.5, 55.5, 36.0, 18.0,
1.0, -14.5, -28.5, -41.5, -53.0, -63.5,
-73.0, -81.5, -88.5, -94.5, -100.0, -104.0,
-107.5, -110.5, -112.0, -113.5, -114.0, -114.0,
-113.5, -112.5, -111.0, -109.0, 106.5, 104.0,
101.0, 98.0, 95.0, 91.5, 88.0, 84.5,
80.5, 77.0, 73.5, 69.5, 66.0, 62.5,
58.5, 55.5, 52.0, 48.5, 45.5, 42.5,
39.5, 36.5, 34.0, 31.5, 29.0, 26.5,
24.5, 22.5, 20.5, 19.0, 17.5, 15.5,
14.5, 13.0, 12.0, 10.5, 9.5, 8.5,
8.0, 7.0, 6.5, 5.5, 5.0, 4.5,
4.0, 3.5, 3.5, 3.0, 2.5, 2.5,
2.0, 2.0, 1.5, 1.5, 1.0, 1.0,
1.0, 1.0, 0.5, 0.5, 0.5, 0.5,
0.5, 0.5
};
// Quantizer lookup, step 1: bitrate classes
static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_1[2][16] = {
// 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384 <- bitrate
{ 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, // mono
// 16, 24, 28, 32, 40, 48, 56, 64, 80, 96,112,128,160,192 <- bitrate / chan
{ 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2 } // stereo
};
// Quantizer lookup, step 2: bitrate class, sample rate -> B2 table idx, sblimit
static const uint8_t PLM_AUDIO_QUANT_TAB_A = (27 | 64); // Table 3-B.2a: high-rate, sblimit = 27
static const uint8_t PLM_AUDIO_QUANT_TAB_B = (30 | 64); // Table 3-B.2b: high-rate, sblimit = 30
static const uint8_t PLM_AUDIO_QUANT_TAB_C = 8; // Table 3-B.2c: low-rate, sblimit = 8
static const uint8_t PLM_AUDIO_QUANT_TAB_D = 12; // Table 3-B.2d: low-rate, sblimit = 12
static const uint8_t QUANT_LUT_STEP_2[3][3] = {
// 44.1 kHz, 48 kHz, 32 kHz
{ PLM_AUDIO_QUANT_TAB_C, PLM_AUDIO_QUANT_TAB_C, PLM_AUDIO_QUANT_TAB_D }, // 32 - 48 kbit/sec/ch
{ PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_A }, // 56 - 80 kbit/sec/ch
{ PLM_AUDIO_QUANT_TAB_B, PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_B } // 96+ kbit/sec/ch
};
// Quantizer lookup, step 3: B2 table, subband -> nbal, row index
// (upper 4 bits: nbal, lower 4 bits: row index)
static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_3[3][32] = {
// Low-rate table (3-B.2c and 3-B.2d)
{
0x44,0x44,
0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34
},
// High-rate table (3-B.2a and 3-B.2b)
{
0x43,0x43,0x43,
0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,
0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,
0x20,0x20,0x20,0x20,0x20,0x20,0x20
},
// MPEG-2 LSR table (B.2 in ISO 13818-3)
{
0x45,0x45,0x45,0x45,
0x34,0x34,0x34,0x34,0x34,0x34,0x34,
0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,
0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24
}
};
// Quantizer lookup, step 4: table row, allocation[] value -> quant table index
static const uint8_t PLM_AUDIO_QUANT_LUT_STEP4[6][16] = {
{ 0, 1, 2, 17 },
{ 0, 1, 2, 3, 4, 5, 6, 17 },
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17 },
{ 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 },
{ 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17 },
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }
};
typedef struct plm_quantizer_spec_t {
unsigned short levels;
unsigned char group;
unsigned char bits;
} plm_quantizer_spec_t;
static const plm_quantizer_spec_t PLM_AUDIO_QUANT_TAB[] = {
{ 3, 1, 5 }, // 1
{ 5, 1, 7 }, // 2
{ 7, 0, 3 }, // 3
{ 9, 1, 10 }, // 4
{ 15, 0, 4 }, // 5
{ 31, 0, 5 }, // 6
{ 63, 0, 6 }, // 7
{ 127, 0, 7 }, // 8
{ 255, 0, 8 }, // 9
{ 511, 0, 9 }, // 10
{ 1023, 0, 10 }, // 11
{ 2047, 0, 11 }, // 12
{ 4095, 0, 12 }, // 13
{ 8191, 0, 13 }, // 14
{ 16383, 0, 14 }, // 15
{ 32767, 0, 15 }, // 16
{ 65535, 0, 16 } // 17
};
struct plm_audio_t {
double time;
int samples_decoded;
int samplerate_index;
int bitrate_index;
int version;
int layer;
int mode;
int bound;
int v_pos;
int next_frame_data_size;
plm_buffer_t *buffer;
int destroy_buffer_when_done;
const plm_quantizer_spec_t *allocation[2][32];
uint8_t scale_factor_info[2][32];
int scale_factor[2][32][3];
int sample[2][32][3];
plm_samples_t samples;
float D[1024];
float V[1024];
float U[32];
} aligned(64);
typedef plm_audio_t plm_audio_t;
int plm_audio_decode_header(plm_audio_t *self);
void plm_audio_decode_frame(plm_audio_t *self);
const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3);
void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part);
void plm_audio_matrix_transform(int s[32][3], int ss, float *d, int dp);
plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) {
plm_audio_t *self = (plm_audio_t *)memalign(alignof(plm_audio_t), sizeof(plm_audio_t));
memset(self, 0, sizeof(plm_audio_t));
self->samples.count = PLM_AUDIO_SAMPLES_PER_FRAME;
self->buffer = buffer;
self->destroy_buffer_when_done = destroy_when_done;
self->samplerate_index = 3; // indicates 0 samplerate
memcpy(self->D, PLM_AUDIO_SYNTHESIS_WINDOW, 512 * sizeof(float));
memcpy(self->D + 512, PLM_AUDIO_SYNTHESIS_WINDOW, 512 * sizeof(float));
// Decode first header
if (plm_buffer_has(self->buffer, 48)) {
self->next_frame_data_size = plm_audio_decode_header(self);
}
return self;
}
void plm_audio_destroy(plm_audio_t *self) {
if (self->destroy_buffer_when_done) {
plm_buffer_destroy(self->buffer);
}
free(self);
}
int plm_audio_get_samplerate(plm_audio_t *self) {
return PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
}
double plm_audio_get_time(plm_audio_t *self) {
return self->time;
}
void plm_audio_rewind(plm_audio_t *self) {
plm_buffer_rewind(self->buffer);
self->time = 0;
self->samples_decoded = 0;
self->next_frame_data_size = 0;
// TODO: needed?
memset(self->V, 0, sizeof(self->V));
memset(self->U, 0, sizeof(self->U));
}
plm_samples_t *plm_audio_decode(plm_audio_t *self) {
DEBUGF("%s", "plm_audio_decode");
// Do we have at least enough information to decode the frame header?
if (!self->next_frame_data_size) {
if (!plm_buffer_has(self->buffer, 48)) {
return NULL;
}
self->next_frame_data_size = plm_audio_decode_header(self);
}
if (
self->next_frame_data_size == 0 ||
!plm_buffer_has(self->buffer, self->next_frame_data_size << 3)
) {
return NULL;
}
plm_audio_decode_frame(self);
self->next_frame_data_size = 0;
self->samples.time = self->time;
self->samples_decoded += PLM_AUDIO_SAMPLES_PER_FRAME;
self->time = (double)self->samples_decoded /
(double)PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
return &self->samples;
}
int plm_audio_decode_header(plm_audio_t *self) {
// Check for valid header: syncword OK, MPEG-Audio Layer 2
plm_buffer_skip_bytes(self->buffer, 0x00);
int sync = plm_buffer_read(self->buffer, 11);
self->version = plm_buffer_read(self->buffer, 2);
self->layer = plm_buffer_read(self->buffer, 2);
int hasCRC = !plm_buffer_read(self->buffer, 1);
if (
sync != PLM_AUDIO_FRAME_SYNC ||
self->version != PLM_AUDIO_MPEG_1 ||
self->layer != PLM_AUDIO_LAYER_II
) {
return false; // Invalid header or unsupported version
}
self->bitrate_index = plm_buffer_read(self->buffer, 4) - 1;
if (self->bitrate_index > 13) {
return false; // Invalid bit rate or 'free format'
}
self->samplerate_index = plm_buffer_read(self->buffer, 2);
if (self->samplerate_index == 3) {
return false; // Invalid sample rate
}
if (self->version == PLM_AUDIO_MPEG_2) {
self->samplerate_index += 4;
self->bitrate_index += 14;
}
int padding = plm_buffer_read(self->buffer, 1);
plm_buffer_skip(self->buffer, 1); // f_private
self->mode = plm_buffer_read(self->buffer, 2);
// Parse the mode_extension, set up the stereo bound
self->bound = 0;
if (self->mode == PLM_AUDIO_MODE_JOINT_STEREO) {
self->bound = (plm_buffer_read(self->buffer, 2) + 1) << 2;
}
else {
plm_buffer_skip(self->buffer, 2);
self->bound = (self->mode == PLM_AUDIO_MODE_MONO) ? 0 : 32;
}
// Discard the last 4 bits of the header and the CRC value, if present
plm_buffer_skip(self->buffer, 4);
if (hasCRC) {
plm_buffer_skip(self->buffer, 16);
}
// Compute frame size, check if we have enough data to decode the whole
// frame.
int bitrate = PLM_AUDIO_BIT_RATE[self->bitrate_index];
int samplerate = PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
int frame_size = (144000 * bitrate / samplerate) + padding;
return frame_size - (hasCRC ? 6 : 4);
}
void plm_audio_decode_frame(plm_audio_t *self) {
// Prepare the quantizer table lookups
int tab3 = 0;
int sblimit = 0;
if (self->version == PLM_AUDIO_MPEG_2) {
// MPEG-2 (LSR)
tab3 = 2;
sblimit = 30;
}
else {
// MPEG-1
int tab1 = (self->mode == PLM_AUDIO_MODE_MONO) ? 0 : 1;
int tab2 = PLM_AUDIO_QUANT_LUT_STEP_1[tab1][self->bitrate_index];
tab3 = QUANT_LUT_STEP_2[tab2][self->samplerate_index];
sblimit = tab3 & 63;
tab3 >>= 6;
}
if (self->bound > sblimit) {
self->bound = sblimit;
}
// Read the allocation information
for (int sb = 0; sb < self->bound; sb++) {
self->allocation[0][sb] = plm_audio_read_allocation(self, sb, tab3);
self->allocation[1][sb] = plm_audio_read_allocation(self, sb, tab3);
}
for (int sb = self->bound; sb < sblimit; sb++) {
self->allocation[0][sb] =
self->allocation[1][sb] =
plm_audio_read_allocation(self, sb, tab3);
}
// Read scale factor selector information
int channels = (self->mode == PLM_AUDIO_MODE_MONO) ? 1 : 2;
for (int sb = 0; sb < sblimit; sb++) {
for (int ch = 0; ch < channels; ch++) {
if (self->allocation[ch][sb]) {
self->scale_factor_info[ch][sb] = plm_buffer_read(self->buffer, 2);
}
}
if (self->mode == PLM_AUDIO_MODE_MONO) {
self->scale_factor_info[1][sb] = self->scale_factor_info[0][sb];
}
}
// Read scale factors
for (int sb = 0; sb < sblimit; sb++) {
for (int ch = 0; ch < channels; ch++) {
if (self->allocation[ch][sb]) {
int *sf = self->scale_factor[ch][sb];
switch (self->scale_factor_info[ch][sb]) {
case 0:
sf[0] = plm_buffer_read(self->buffer, 6);
sf[1] = plm_buffer_read(self->buffer, 6);
sf[2] = plm_buffer_read(self->buffer, 6);
break;
case 1:
sf[0] =
sf[1] = plm_buffer_read(self->buffer, 6);
sf[2] = plm_buffer_read(self->buffer, 6);
break;
case 2:
sf[0] =
sf[1] =
sf[2] = plm_buffer_read(self->buffer, 6);
break;
case 3:
sf[0] = plm_buffer_read(self->buffer, 6);
sf[1] =
sf[2] = plm_buffer_read(self->buffer, 6);
break;
}
}
}
if (self->mode == PLM_AUDIO_MODE_MONO) {
self->scale_factor[1][sb][0] = self->scale_factor[0][sb][0];
self->scale_factor[1][sb][1] = self->scale_factor[0][sb][1];
self->scale_factor[1][sb][2] = self->scale_factor[0][sb][2];
}
}
// Coefficient input and reconstruction
int out_pos = 0;
for (int part = 0; part < 3; part++) {
for (int granule = 0; granule < 4; granule++) {
// Read the samples
for (int sb = 0; sb < self->bound; sb++) {
plm_audio_read_samples(self, 0, sb, part);
plm_audio_read_samples(self, 1, sb, part);
}
for (int sb = self->bound; sb < sblimit; sb++) {
plm_audio_read_samples(self, 0, sb, part);
self->sample[1][sb][0] = self->sample[0][sb][0];
self->sample[1][sb][1] = self->sample[0][sb][1];
self->sample[1][sb][2] = self->sample[0][sb][2];
}
for (int sb = sblimit; sb < 32; sb++) {
self->sample[0][sb][0] = 0;
self->sample[0][sb][1] = 0;
self->sample[0][sb][2] = 0;
self->sample[1][sb][0] = 0;
self->sample[1][sb][1] = 0;
self->sample[1][sb][2] = 0;
}
// Synthesis loop
for (int p = 0; p < 3; p++) {
// Shifting step
self->v_pos = (self->v_pos - 64) & 1023;
for (int ch = 0; ch < 2; ch++) {
plm_audio_matrix_transform(self->sample[ch], p, self->V, self->v_pos);
// Build U, windowing, calculate output
memset(self->U, 0, sizeof(self->U));
int d_index = 512 - (self->v_pos >> 1);
int v_index = (self->v_pos % 128) >> 1;
while (v_index < 1024) {
for (int i = 0; i < 32; ++i) {
self->U[i] += self->D[d_index++] * self->V[v_index++];
}
v_index += 128 - 32;
d_index += 64 - 32;
}
d_index -= (512 - 32);
v_index = (128 - 32 + 1024) - v_index;
while (v_index < 1024) {
for (int i = 0; i < 32; ++i) {
self->U[i] += self->D[d_index++] * self->V[v_index++];
}
v_index += 128 - 32;
d_index += 64 - 32;
}
// Output samples
#ifdef PLM_AUDIO_SEPARATE_CHANNELS
float *out_channel = ch == 0
? self->samples.left
: self->samples.right;
for (int j = 0; j < 32; j++) {
out_channel[out_pos + j] = self->U[j] / 2147418112.0f;
}
#else
for (int j = 0; j < 32; j++) {
self->samples.interleaved[((out_pos + j) << 1) + ch] =
self->U[j] / 2147418112.0f;
}
#endif
} // End of synthesis channel loop
out_pos += 32;
} // End of synthesis sub-block loop
} // Decoding of the granule finished
}
plm_buffer_align(self->buffer);
}
const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3) {
int tab4 = PLM_AUDIO_QUANT_LUT_STEP_3[tab3][sb];
int qtab = PLM_AUDIO_QUANT_LUT_STEP4[tab4 & 15][plm_buffer_read(self->buffer, tab4 >> 4)];
return qtab ? (&PLM_AUDIO_QUANT_TAB[qtab - 1]) : 0;
}
void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part) {
const plm_quantizer_spec_t *q = self->allocation[ch][sb];
int sf = self->scale_factor[ch][sb][part];
int *sample = self->sample[ch][sb];
int val = 0;
if (!q) {
// No bits allocated for this subband
sample[0] = sample[1] = sample[2] = 0;
return;
}
// Resolve scalefactor
if (sf == 63) {
sf = 0;
}
else {
int shift = (sf / 3) | 0;
sf = (PLM_AUDIO_SCALEFACTOR_BASE[sf % 3] + ((1u << shift) >> 1)) >> shift;
}
// Decode samples
int adj = q->levels;
if (q->group) {
// Decode grouped samples
val = plm_buffer_read(self->buffer, q->bits);
sample[0] = val % adj;
val /= adj;
sample[1] = val % adj;
sample[2] = val / adj;
}
else {
// Decode direct samples
sample[0] = plm_buffer_read(self->buffer, q->bits);
sample[1] = plm_buffer_read(self->buffer, q->bits);
sample[2] = plm_buffer_read(self->buffer, q->bits);
}
// Postmultiply samples
int scale = 65536 / (adj + 1);
adj = ((adj + 1) >> 1) - 1;
val = (adj - sample[0]) * scale;
sample[0] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
val = (adj - sample[1]) * scale;
sample[1] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
val = (adj - sample[2]) * scale;
sample[2] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
}
void plm_audio_matrix_transform(int s[32][3], int ss, float *d, int dp) {
float t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12,
t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24,
t25, t26, t27, t28, t29, t30, t31, t32, t33;
t01 = (float)(s[0][ss] + s[31][ss]); t02 = (float)(s[0][ss] - s[31][ss]) * 0.500602998235f;
t03 = (float)(s[1][ss] + s[30][ss]); t04 = (float)(s[1][ss] - s[30][ss]) * 0.505470959898f;
t05 = (float)(s[2][ss] + s[29][ss]); t06 = (float)(s[2][ss] - s[29][ss]) * 0.515447309923f;
t07 = (float)(s[3][ss] + s[28][ss]); t08 = (float)(s[3][ss] - s[28][ss]) * 0.53104259109f;
t09 = (float)(s[4][ss] + s[27][ss]); t10 = (float)(s[4][ss] - s[27][ss]) * 0.553103896034f;
t11 = (float)(s[5][ss] + s[26][ss]); t12 = (float)(s[5][ss] - s[26][ss]) * 0.582934968206f;
t13 = (float)(s[6][ss] + s[25][ss]); t14 = (float)(s[6][ss] - s[25][ss]) * 0.622504123036f;
t15 = (float)(s[7][ss] + s[24][ss]); t16 = (float)(s[7][ss] - s[24][ss]) * 0.674808341455f;
t17 = (float)(s[8][ss] + s[23][ss]); t18 = (float)(s[8][ss] - s[23][ss]) * 0.744536271002f;
t19 = (float)(s[9][ss] + s[22][ss]); t20 = (float)(s[9][ss] - s[22][ss]) * 0.839349645416f;
t21 = (float)(s[10][ss] + s[21][ss]); t22 = (float)(s[10][ss] - s[21][ss]) * 0.972568237862f;
t23 = (float)(s[11][ss] + s[20][ss]); t24 = (float)(s[11][ss] - s[20][ss]) * 1.16943993343f;
t25 = (float)(s[12][ss] + s[19][ss]); t26 = (float)(s[12][ss] - s[19][ss]) * 1.48416461631f;
t27 = (float)(s[13][ss] + s[18][ss]); t28 = (float)(s[13][ss] - s[18][ss]) * 2.05778100995f;
t29 = (float)(s[14][ss] + s[17][ss]); t30 = (float)(s[14][ss] - s[17][ss]) * 3.40760841847f;
t31 = (float)(s[15][ss] + s[16][ss]); t32 = (float)(s[15][ss] - s[16][ss]) * 10.1900081235f;
t33 = t01 + t31; t31 = (t01 - t31) * 0.502419286188f;
t01 = t03 + t29; t29 = (t03 - t29) * 0.52249861494f;
t03 = t05 + t27; t27 = (t05 - t27) * 0.566944034816f;
t05 = t07 + t25; t25 = (t07 - t25) * 0.64682178336f;
t07 = t09 + t23; t23 = (t09 - t23) * 0.788154623451f;
t09 = t11 + t21; t21 = (t11 - t21) * 1.06067768599f;
t11 = t13 + t19; t19 = (t13 - t19) * 1.72244709824f;
t13 = t15 + t17; t17 = (t15 - t17) * 5.10114861869f;
t15 = t33 + t13; t13 = (t33 - t13) * 0.509795579104f;
t33 = t01 + t11; t01 = (t01 - t11) * 0.601344886935f;
t11 = t03 + t09; t09 = (t03 - t09) * 0.899976223136f;
t03 = t05 + t07; t07 = (t05 - t07) * 2.56291544774f;
t05 = t15 + t03; t15 = (t15 - t03) * 0.541196100146f;
t03 = t33 + t11; t11 = (t33 - t11) * 1.30656296488f;
t33 = t05 + t03; t05 = (t05 - t03) * 0.707106781187f;
t03 = t15 + t11; t15 = (t15 - t11) * 0.707106781187f;
t03 += t15;
t11 = t13 + t07; t13 = (t13 - t07) * 0.541196100146f;
t07 = t01 + t09; t09 = (t01 - t09) * 1.30656296488f;
t01 = t11 + t07; t07 = (t11 - t07) * 0.707106781187f;
t11 = t13 + t09; t13 = (t13 - t09) * 0.707106781187f;
t11 += t13; t01 += t11;
t11 += t07; t07 += t13;
t09 = t31 + t17; t31 = (t31 - t17) * 0.509795579104f;
t17 = t29 + t19; t29 = (t29 - t19) * 0.601344886935f;
t19 = t27 + t21; t21 = (t27 - t21) * 0.899976223136f;
t27 = t25 + t23; t23 = (t25 - t23) * 2.56291544774f;
t25 = t09 + t27; t09 = (t09 - t27) * 0.541196100146f;
t27 = t17 + t19; t19 = (t17 - t19) * 1.30656296488f;
t17 = t25 + t27; t27 = (t25 - t27) * 0.707106781187f;
t25 = t09 + t19; t19 = (t09 - t19) * 0.707106781187f;
t25 += t19;
t09 = t31 + t23; t31 = (t31 - t23) * 0.541196100146f;
t23 = t29 + t21; t21 = (t29 - t21) * 1.30656296488f;
t29 = t09 + t23; t23 = (t09 - t23) * 0.707106781187f;
t09 = t31 + t21; t31 = (t31 - t21) * 0.707106781187f;
t09 += t31; t29 += t09; t09 += t23; t23 += t31;
t17 += t29; t29 += t25; t25 += t09; t09 += t27;
t27 += t23; t23 += t19; t19 += t31;
t21 = t02 + t32; t02 = (t02 - t32) * 0.502419286188f;
t32 = t04 + t30; t04 = (t04 - t30) * 0.52249861494f;
t30 = t06 + t28; t28 = (t06 - t28) * 0.566944034816f;
t06 = t08 + t26; t08 = (t08 - t26) * 0.64682178336f;
t26 = t10 + t24; t10 = (t10 - t24) * 0.788154623451f;
t24 = t12 + t22; t22 = (t12 - t22) * 1.06067768599f;
t12 = t14 + t20; t20 = (t14 - t20) * 1.72244709824f;
t14 = t16 + t18; t16 = (t16 - t18) * 5.10114861869f;
t18 = t21 + t14; t14 = (t21 - t14) * 0.509795579104f;
t21 = t32 + t12; t32 = (t32 - t12) * 0.601344886935f;
t12 = t30 + t24; t24 = (t30 - t24) * 0.899976223136f;
t30 = t06 + t26; t26 = (t06 - t26) * 2.56291544774f;
t06 = t18 + t30; t18 = (t18 - t30) * 0.541196100146f;
t30 = t21 + t12; t12 = (t21 - t12) * 1.30656296488f;
t21 = t06 + t30; t30 = (t06 - t30) * 0.707106781187f;
t06 = t18 + t12; t12 = (t18 - t12) * 0.707106781187f;
t06 += t12;
t18 = t14 + t26; t26 = (t14 - t26) * 0.541196100146f;
t14 = t32 + t24; t24 = (t32 - t24) * 1.30656296488f;
t32 = t18 + t14; t14 = (t18 - t14) * 0.707106781187f;
t18 = t26 + t24; t24 = (t26 - t24) * 0.707106781187f;
t18 += t24; t32 += t18;
t18 += t14; t26 = t14 + t24;
t14 = t02 + t16; t02 = (t02 - t16) * 0.509795579104f;
t16 = t04 + t20; t04 = (t04 - t20) * 0.601344886935f;
t20 = t28 + t22; t22 = (t28 - t22) * 0.899976223136f;
t28 = t08 + t10; t10 = (t08 - t10) * 2.56291544774f;
t08 = t14 + t28; t14 = (t14 - t28) * 0.541196100146f;
t28 = t16 + t20; t20 = (t16 - t20) * 1.30656296488f;
t16 = t08 + t28; t28 = (t08 - t28) * 0.707106781187f;
t08 = t14 + t20; t20 = (t14 - t20) * 0.707106781187f;
t08 += t20;
t14 = t02 + t10; t02 = (t02 - t10) * 0.541196100146f;
t10 = t04 + t22; t22 = (t04 - t22) * 1.30656296488f;
t04 = t14 + t10; t10 = (t14 - t10) * 0.707106781187f;
t14 = t02 + t22; t02 = (t02 - t22) * 0.707106781187f;
t14 += t02; t04 += t14; t14 += t10; t10 += t02;
t16 += t04; t04 += t08; t08 += t14; t14 += t28;
t28 += t10; t10 += t20; t20 += t02; t21 += t16;
t16 += t32; t32 += t04; t04 += t06; t06 += t08;
t08 += t18; t18 += t14; t14 += t30; t30 += t28;
t28 += t26; t26 += t10; t10 += t12; t12 += t20;
t20 += t24; t24 += t02;
d[dp + 48] = -t33;
d[dp + 49] = d[dp + 47] = -t21;
d[dp + 50] = d[dp + 46] = -t17;
d[dp + 51] = d[dp + 45] = -t16;
d[dp + 52] = d[dp + 44] = -t01;
d[dp + 53] = d[dp + 43] = -t32;
d[dp + 54] = d[dp + 42] = -t29;
d[dp + 55] = d[dp + 41] = -t04;
d[dp + 56] = d[dp + 40] = -t03;
d[dp + 57] = d[dp + 39] = -t06;
d[dp + 58] = d[dp + 38] = -t25;
d[dp + 59] = d[dp + 37] = -t08;
d[dp + 60] = d[dp + 36] = -t11;
d[dp + 61] = d[dp + 35] = -t18;
d[dp + 62] = d[dp + 34] = -t09;
d[dp + 63] = d[dp + 33] = -t14;
d[dp + 32] = -t05;
d[dp + 0] = t05; d[dp + 31] = -t30;
d[dp + 1] = t30; d[dp + 30] = -t27;
d[dp + 2] = t27; d[dp + 29] = -t28;
d[dp + 3] = t28; d[dp + 28] = -t07;
d[dp + 4] = t07; d[dp + 27] = -t26;
d[dp + 5] = t26; d[dp + 26] = -t23;
d[dp + 6] = t23; d[dp + 25] = -t10;
d[dp + 7] = t10; d[dp + 24] = -t15;
d[dp + 8] = t15; d[dp + 23] = -t12;
d[dp + 9] = t12; d[dp + 22] = -t19;
d[dp + 10] = t19; d[dp + 21] = -t20;
d[dp + 11] = t20; d[dp + 20] = -t13;
d[dp + 12] = t13; d[dp + 19] = -t24;
d[dp + 13] = t24; d[dp + 18] = -t31;
d[dp + 14] = t31; d[dp + 17] = -t02;
d[dp + 15] = t02; d[dp + 16] = 0.0;
};

450
dsp/mpeg/mpeg.h Normal file
View file

@ -0,0 +1,450 @@
#ifndef COSMOPOLITAN_DSP_MPEG_MPEG_H_
#define COSMOPOLITAN_DSP_MPEG_MPEG_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct FILE;
typedef struct plm_t plm_t;
typedef struct plm_buffer_t plm_buffer_t;
typedef struct plm_demux_t plm_demux_t;
typedef struct plm_video_t plm_video_t;
typedef struct plm_audio_t plm_audio_t;
/**
* Demuxed MPEG PS packet
*
* The type maps directly to the various MPEG-PES start codes. pts is
* the presentation time stamp of the packet in seconds. Not all packets
* have a pts value.
*/
typedef struct plm_packet_t {
int type;
double pts;
size_t length;
uint8_t *data;
} plm_packet_t;
/**
* Decoded Video Plane
*
* The byte length of the data is width * height. Note that different
* planes have different sizes: the Luma plane (Y) is double the size of
* each of the two Chroma planes (Cr, Cb) - i.e. 4 times the byte
* length. Also note that the size of the plane does *not* denote the
* size of the displayed frame. The sizes of planes are always rounded
* up to the nearest macroblock (16px).
*/
typedef struct plm_plane_t {
unsigned int width;
unsigned int height;
uint8_t *data;
} plm_plane_t;
/**
* Decoded Video Frame
*
* Width and height denote the desired display size of the frame. This
* may be different from the internal size of the 3 planes.
*/
typedef struct plm_frame_t {
double time;
unsigned int width;
unsigned int height;
plm_plane_t y;
plm_plane_t cr;
plm_plane_t cb;
} plm_frame_t;
/**
* Callback function type for decoded video frames used by the high-level
* plm_* interface
*/
typedef void (*plm_video_decode_callback)(plm_t *self, plm_frame_t *frame,
void *user);
/**
* Decoded Audio Samples
*
* Samples are stored as normalized (-1, 1) float either interleaved, or if
* PLM_AUDIO_SEPARATE_CHANNELS is defined, in two separate arrays.
* The `count` is always PLM_AUDIO_SAMPLES_PER_FRAME and just there for
* convenience.
*/
#define PLM_AUDIO_SAMPLES_PER_FRAME 1152
struct plm_samples_t {
double time;
unsigned int count;
#ifdef PLM_AUDIO_SEPARATE_CHANNELS
float left[PLM_AUDIO_SAMPLES_PER_FRAME] aligned(32);
float right[PLM_AUDIO_SAMPLES_PER_FRAME] aligned(32);
#else
float interleaved[PLM_AUDIO_SAMPLES_PER_FRAME * 2] aligned(32);
#endif
} aligned(32);
typedef struct plm_samples_t plm_samples_t;
/**
* Callback function type for decoded audio samples used by the high-level
* plm_* interface
*/
typedef void (*plm_audio_decode_callback)(plm_t *self, plm_samples_t *samples,
void *user);
/**
* Callback function for plm_buffer when it needs more data
*/
typedef void (*plm_buffer_load_callback)(plm_buffer_t *self, void *user);
/**
* -----------------------------------------------------------------------------
* plm_* public API
* High-Level API for loading/demuxing/decoding MPEG-PS data
*
* Create a plmpeg instance with a filename. Returns NULL if the file could not
* be opened.
*/
plm_t *plm_create_with_filename(const char *filename);
/**
* Create a plmpeg instance with file handle. Pass true to close_when_done
* to let plmpeg call fclose() on the handle when plm_destroy() is
* called.
*/
plm_t *plm_create_with_file(struct FILE *fh, int close_when_done);
/**
* Create a plmpeg instance with pointer to memory as source. This assumes the
* whole file is in memory. Pass true to free_when_done to let plmpeg call
* free() on the pointer when plm_destroy() is called.
*/
plm_t *plm_create_with_memory(uint8_t *bytes, size_t length,
int free_when_done);
/**
* Create a plmpeg instance with a plm_buffer as source. This is also
* called internally by all the above constructor functions.
*/
plm_t *plm_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done);
/**
* Destroy a plmpeg instance and free all data
*/
void plm_destroy(plm_t *self);
/**
* Get or set whether video decoding is enabled.
*/
int plm_get_video_enabled(plm_t *self);
void plm_set_video_enabled(plm_t *self, int enabled);
/**
* Get or set whether audio decoding is enabled. When enabling, you can set the
* desired audio stream (0-3) to decode.
*/
int plm_get_audio_enabled(plm_t *self);
void plm_set_audio_enabled(plm_t *self, int enabled, int stream_index);
/**
* Get the display width/height of the video stream
*/
int plm_get_width(plm_t *self);
int plm_get_height(plm_t *self);
double plm_get_pixel_aspect_ratio(plm_t *);
/**
* Get the framerate of the video stream in frames per second
*/
double plm_get_framerate(plm_t *self);
/**
* Get the number of available audio streams in the file
*/
int plm_get_num_audio_streams(plm_t *self);
/**
* Get the samplerate of the audio stream in samples per second
*/
int plm_get_samplerate(plm_t *self);
/**
* Get or set the audio lead time in seconds - the time in which audio samples
* are decoded in advance (or behind) the video decode time. Default 0.
*/
double plm_get_audio_lead_time(plm_t *self);
void plm_set_audio_lead_time(plm_t *self, double lead_time);
/**
* Get the current internal time in seconds
*/
double plm_get_time(plm_t *self);
/**
* Rewind all buffers back to the beginning.
*/
void plm_rewind(plm_t *self);
/**
* Get or set looping. Default false.
*/
int plm_get_loop(plm_t *self);
void plm_set_loop(plm_t *self, int loop);
/**
* Get whether the file has ended. If looping is enabled, this will always
* return false.
*/
int plm_has_ended(plm_t *self);
/**
* Set the callback for decoded video frames used with plm_decode(). If no
* callback is set, video data will be ignored and not be decoded.
*/
void plm_set_video_decode_callback(plm_t *self, plm_video_decode_callback fp,
void *user);
/**
* Set the callback for decoded audio samples used with plm_decode(). If no
* callback is set, audio data will be ignored and not be decoded.
*/
void plm_set_audio_decode_callback(plm_t *self, plm_audio_decode_callback fp,
void *user);
/**
* Advance the internal timer by seconds and decode video/audio up to
* this time. Returns true/false whether anything was decoded.
*/
int plm_decode(plm_t *self, double seconds);
/**
* Decode and return one video frame. Returns NULL if no frame could be decoded
* (either because the source ended or data is corrupt). If you only want to
* decode video, you should disable audio via plm_set_audio_enabled().
* The returned plm_frame_t is valid until the next call to
* plm_decode_video call or until the plm_destroy is called.
*/
plm_frame_t *plm_decode_video(plm_t *self);
/**
* Decode and return one audio frame. Returns NULL if no frame could be decoded
* (either because the source ended or data is corrupt). If you only want to
* decode audio, you should disable video via plm_set_video_enabled().
* The returned plm_samples_t is valid until the next call to
* plm_decode_video or until the plm_destroy is called.
*/
plm_samples_t *plm_decode_audio(plm_t *self);
/* -----------------------------------------------------------------------------
* plm_buffer public API
* Provides the data source for all other plm_* interfaces
*
* The default size for buffers created from files or by the high-level API
*/
#ifndef PLM_BUFFER_DEFAULT_SIZE
#define PLM_BUFFER_DEFAULT_SIZE (128 * 1024)
#endif
/**
* Create a buffer instance with a filename. Returns NULL if the file could not
* be opened.
*/
plm_buffer_t *plm_buffer_create_with_filename(const char *filename);
/**
* Create a buffer instance with file handle. Pass true to close_when_done
* to let plmpeg call fclose() on the handle when plm_destroy() is
* called.
*/
plm_buffer_t *plm_buffer_create_with_file(struct FILE *fh, int close_when_done);
/**
* Create a buffer instance with a pointer to memory as source. This assumes
* the whole file is in memory. Pass 1 to free_when_done to let plmpeg call
* free() on the pointer when plm_destroy() is called.
*/
plm_buffer_t *plm_buffer_create_with_memory(uint8_t *bytes, size_t length,
int free_when_done);
/**
* Create an empty buffer with an initial capacity. The buffer will grow
* as needed.
*/
plm_buffer_t *plm_buffer_create_with_capacity(size_t capacity);
/**
* Destroy a buffer instance and free all data
*/
void plm_buffer_destroy(plm_buffer_t *self);
/**
* Copy data into the buffer. If the data to be written is larger than the
* available space, the buffer will realloc() with a larger capacity.
* Returns the number of bytes written. This will always be the same as the
* passed in length, except when the buffer was created _with_memory() for
* which _write() is forbidden.
*/
size_t plm_buffer_write(plm_buffer_t *self, uint8_t *bytes, size_t length);
/**
* Set a callback that is called whenever the buffer needs more data
*/
void plm_buffer_set_load_callback(plm_buffer_t *self,
plm_buffer_load_callback fp, void *user);
/**
* Rewind the buffer back to the beginning. When loading from a file handle,
* this also seeks to the beginning of the file.
*/
void plm_buffer_rewind(plm_buffer_t *self);
/**
* -----------------------------------------------------------------------------
* plm_demux public API
* Demux an MPEG Program Stream (PS) data into separate packages
*
* Various Packet Types
*/
#define PLM_DEMUX_PACKET_PRIVATE 0xBD
#define PLM_DEMUX_PACKET_AUDIO_1 0xC0
#define PLM_DEMUX_PACKET_AUDIO_2 0xC1
#define PLM_DEMUX_PACKET_AUDIO_3 0xC2
#define PLM_DEMUX_PACKET_AUDIO_4 0xC2
#define PLM_DEMUX_PACKET_VIDEO_1 0xE0
/**
* Create a demuxer with a plm_buffer as source
*/
plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done);
/**
* Destroy a demuxer and free all data
*/
void plm_demux_destroy(plm_demux_t *self);
/**
* Returns the number of video streams found in the system header.
*/
int plm_demux_get_num_video_streams(plm_demux_t *self);
/**
* Returns the number of audio streams found in the system header.
*/
int plm_demux_get_num_audio_streams(plm_demux_t *self);
/**
* Rewinds the internal buffer. See plm_buffer_rewind().
*/
void plm_demux_rewind(plm_demux_t *self);
/**
* Decode and return the next packet. The returned packet_t is valid until
* the next call to plm_demux_decode() or until the demuxer is destroyed.
*/
plm_packet_t *plm_demux_decode(plm_demux_t *self);
/* -----------------------------------------------------------------------------
* plm_video public API
* Decode MPEG1 Video ("mpeg1") data into raw YCrCb frames
*/
/**
* Create a video decoder with a plm_buffer as source
*/
plm_video_t *plm_video_create_with_buffer(plm_buffer_t *buffer,
int destroy_when_done);
/**
* Destroy a video decoder and free all data
*/
void plm_video_destroy(plm_video_t *self);
/**
* Get the framerate in frames per second
*/
double plm_video_get_framerate(plm_video_t *);
double plm_video_get_pixel_aspect_ratio(plm_video_t *);
/**
* Get the display width/height
*/
int plm_video_get_width(plm_video_t *);
int plm_video_get_height(plm_video_t *);
/**
* Set "no delay" mode. When enabled, the decoder assumes that the video does
* *not* contain any B-Frames. This is useful for reducing lag when streaming.
*/
void plm_video_set_no_delay(plm_video_t *self, int no_delay);
/**
* Get the current internal time in seconds
*/
double plm_video_get_time(plm_video_t *self);
/**
* Rewinds the internal buffer. See plm_buffer_rewind().
*/
void plm_video_rewind(plm_video_t *self);
/**
* Decode and return one frame of video and advance the internal time by
* 1/framerate seconds. The returned frame_t is valid until the next call of
* plm_video_decode() or until the video decoder is destroyed.
*/
plm_frame_t *plm_video_decode(plm_video_t *self);
/**
* Convert the YCrCb data of a frame into an interleaved RGB buffer. The buffer
* pointed to by *rgb must have a size of (frame->width * frame->height * 3)
* bytes.
*/
void plm_frame_to_rgb(plm_frame_t *frame, uint8_t *rgb);
/* -----------------------------------------------------------------------------
* plm_audio public API
* Decode MPEG-1 Audio Layer II ("mp2") data into raw samples
*/
/**
* Create an audio decoder with a plm_buffer as source.
*/
plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer,
int destroy_when_done);
/**
* Destroy an audio decoder and free all data
*/
void plm_audio_destroy(plm_audio_t *self);
/**
* Get the samplerate in samples per second
*/
int plm_audio_get_samplerate(plm_audio_t *self);
/**
* Get the current internal time in seconds
*/
double plm_audio_get_time(plm_audio_t *self);
/**
* Rewinds the internal buffer. See plm_buffer_rewind().
*/
void plm_audio_rewind(plm_audio_t *self);
/**
* Decode and return one "frame" of audio and advance the internal time by
* (PLM_AUDIO_SAMPLES_PER_FRAME/samplerate) seconds. The returned samples_t
* is valid until the next call of plm_audio_decode() or until the audio
* decoder is destroyed.
*/
plm_samples_t *plm_audio_decode(plm_audio_t *self);
extern long plmpegdecode_latency_;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_MPEG_MPEG_H_ */

69
dsp/mpeg/mpeg.mk Normal file
View file

@ -0,0 +1,69 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += DSP_MPEG
DSP_MPEG_ARTIFACTS += DSP_MPEG_A
DSP_MPEG = $(DSP_MPEG_A_DEPS) $(DSP_MPEG_A)
DSP_MPEG_A = o/$(MODE)/dsp/mpeg/mpeg.a
DSP_MPEG_A_FILES := $(wildcard dsp/mpeg/*)
DSP_MPEG_A_HDRS = $(filter %.h,$(DSP_MPEG_A_FILES))
DSP_MPEG_A_SRCS_S = $(filter %.S,$(DSP_MPEG_A_FILES))
DSP_MPEG_A_SRCS_C = $(filter %.c,$(DSP_MPEG_A_FILES))
DSP_MPEG_A_SRCS = \
$(DSP_MPEG_A_SRCS_S) \
$(DSP_MPEG_A_SRCS_C)
DSP_MPEG_A_OBJS = \
$(DSP_MPEG_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(DSP_MPEG_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(DSP_MPEG_A_SRCS_C:%.c=o/$(MODE)/%.o)
DSP_MPEG_A_CHECKS = \
$(DSP_MPEG_A).pkg \
$(DSP_MPEG_A_HDRS:%=o/$(MODE)/%.ok)
DSP_MPEG_A_DIRECTDEPS = \
LIBC_CALLS \
LIBC_LOG \
LIBC_RUNTIME \
LIBC_TINYMATH \
LIBC_TIME \
LIBC_STUBS \
LIBC_STR \
LIBC_NEXGEN32E \
LIBC_STDIO \
LIBC_SYSV \
LIBC_MEM \
LIBC_LOG \
LIBC_FMT \
LIBC_UNICODE \
DSP_MPEG_A_DEPS := \
$(call uniq,$(foreach x,$(DSP_MPEG_A_DIRECTDEPS),$($(x))))
$(DSP_MPEG_A): dsp/mpeg/ \
$(DSP_MPEG_A).pkg \
$(DSP_MPEG_A_OBJS)
$(DSP_MPEG_A).pkg: \
$(DSP_MPEG_A_OBJS) \
$(foreach x,$(DSP_MPEG_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/dsp/mpeg/clamp4int256-k8.o: \
OVERRIDE_CFLAGS += \
-Os
#o/$(MODE)/dsp/mpeg/macroblock.o: \
CC = $(CLANG)
DSP_MPEG_LIBS = $(foreach x,$(DSP_MPEG_ARTIFACTS),$($(x)))
DSP_MPEG_SRCS = $(foreach x,$(DSP_MPEG_ARTIFACTS),$($(x)_SRCS))
DSP_MPEG_HDRS = $(foreach x,$(DSP_MPEG_ARTIFACTS),$($(x)_HDRS))
DSP_MPEG_CHECKS = $(foreach x,$(DSP_MPEG_ARTIFACTS),$($(x)_CHECKS))
DSP_MPEG_OBJS = $(foreach x,$(DSP_MPEG_ARTIFACTS),$($(x)_OBJS))
$(DSP_MPEG_OBJS): $(BUILD_FILES) dsp/mpeg/mpeg.mk
.PHONY: o/$(MODE)/dsp/mpeg
o/$(MODE)/dsp/mpeg: $(DSP_MPEG_CHECKS)

1115
dsp/mpeg/mpeg1.c Normal file

File diff suppressed because it is too large Load diff

337
dsp/mpeg/plm.c Normal file
View file

@ -0,0 +1,337 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
vi: set et ft=c ts=4 sw=4 fenc=utf-8 :vi
PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
Dominic Szablewski - https://phoboslab.org │
The MIT License(MIT)
Copyright(c) 2019 Dominic Szablewski
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 "dsp/mpeg/mpeg.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
asm(".ident\t\"\\n\\n\
PL_MPEG (MIT License)\\n\
Copyright(c) 2019 Dominic Szablewski\\n\
https://phoboslab.org\"");
asm(".include \"libc/disclaimer.inc\"");
/* clang-format off */
// -----------------------------------------------------------------------------
// plm (high-level interface) implementation
typedef struct plm_t {
plm_demux_t *demux;
double time;
int has_ended;
int loop;
int video_packet_type;
plm_buffer_t *video_buffer;
plm_video_t *video_decoder;
int audio_packet_type;
double audio_lead_time;
plm_buffer_t *audio_buffer;
plm_audio_t *audio_decoder;
plm_video_decode_callback video_decode_callback;
void *video_decode_callback_user_data;
plm_audio_decode_callback audio_decode_callback;
void *audio_decode_callback_user_data;
} plm_t;
void plm_handle_end(plm_t *self);
void plm_read_video_packet(plm_buffer_t *buffer, void *user);
void plm_read_audio_packet(plm_buffer_t *buffer, void *user);
void plm_read_packets(plm_t *self, int requested_type);
plm_t *plm_create_with_filename(const char *filename) {
plm_buffer_t *buffer = plm_buffer_create_with_filename(filename);
if (!buffer) {
return NULL;
}
return plm_create_with_buffer(buffer, true);
}
plm_t *plm_create_with_file(FILE *fh, int close_when_done) {
plm_buffer_t *buffer = plm_buffer_create_with_file(fh, close_when_done);
return plm_create_with_buffer(buffer, true);
}
plm_t *plm_create_with_memory(uint8_t *bytes, size_t length, int free_when_done) {
plm_buffer_t *buffer = plm_buffer_create_with_memory(bytes, length, free_when_done);
return plm_create_with_buffer(buffer, true);
}
plm_t *plm_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) {
plm_t *self = (plm_t *)malloc(sizeof(plm_t));
memset(self, 0, sizeof(plm_t));
self->demux = plm_demux_create(buffer, destroy_when_done);
// In theory we should check plm_demux_get_num_video_streams() and
// plm_demux_get_num_audio_streams() here, but older files typically
// do not specify these correcly. So we just assume we have a video and
// audio stream and create the decoders.
self->video_packet_type = PLM_DEMUX_PACKET_VIDEO_1;
self->video_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
plm_buffer_set_load_callback(self->video_buffer, plm_read_video_packet, self);
self->audio_packet_type = PLM_DEMUX_PACKET_AUDIO_1;
self->audio_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
plm_buffer_set_load_callback(self->audio_buffer, plm_read_audio_packet, self);
self->video_decoder = plm_video_create_with_buffer(self->video_buffer, true);
self->audio_decoder = plm_audio_create_with_buffer(self->audio_buffer, true);
return self;
}
void plm_destroy(plm_t *self) {
plm_video_destroy(self->video_decoder);
plm_audio_destroy(self->audio_decoder);
plm_demux_destroy(self->demux);
free(self);
}
int plm_get_audio_enabled(plm_t *self) {
return (self->audio_packet_type != 0);
}
void plm_set_audio_enabled(plm_t *self, int enabled, int stream_index) {
/* int num_streams = plm_demux_get_num_audio_streams(self->demux); */
self->audio_packet_type = (enabled && stream_index >= 0 && stream_index < 4)
? PLM_DEMUX_PACKET_AUDIO_1 + stream_index
: 0;
}
int plm_get_video_enabled(plm_t *self) {
return (self->video_packet_type != 0);
}
void plm_set_video_enabled(plm_t *self, int enabled) {
self->video_packet_type = (enabled)
? PLM_DEMUX_PACKET_VIDEO_1
: 0;
}
int plm_get_width(plm_t *self) {
return plm_video_get_width(self->video_decoder);
}
double plm_get_pixel_aspect_ratio(plm_t *self) {
return plm_video_get_pixel_aspect_ratio(self->video_decoder);
}
int plm_get_height(plm_t *self) {
return plm_video_get_height(self->video_decoder);
}
double plm_get_framerate(plm_t *self) {
return plm_video_get_framerate(self->video_decoder);
}
int plm_get_num_audio_streams(plm_t *self) {
// Some files do not specify the number of audio streams in the system header.
// If the reported number of streams is 0, we check if we have a samplerate,
// indicating at least one audio stream.
int num_streams = plm_demux_get_num_audio_streams(self->demux);
return num_streams == 0 && plm_get_samplerate(self) ? 1 : num_streams;
}
int plm_get_samplerate(plm_t *self) {
return plm_audio_get_samplerate(self->audio_decoder);
}
double plm_get_audio_lead_time(plm_t *self) {
return self->audio_lead_time;
}
void plm_set_audio_lead_time(plm_t *self, double lead_time) {
self->audio_lead_time = lead_time;
}
double plm_get_time(plm_t *self) {
return self->time;
}
void plm_rewind(plm_t *self) {
plm_video_rewind(self->video_decoder);
plm_audio_rewind(self->audio_decoder);
plm_demux_rewind(self->demux);
self->time = 0;
}
int plm_get_loop(plm_t *self) {
return self->loop;
}
void plm_set_loop(plm_t *self, int loop) {
self->loop = loop;
}
int plm_has_ended(plm_t *self) {
return self->has_ended;
}
void plm_set_video_decode_callback(plm_t *self, plm_video_decode_callback fp, void *user) {
self->video_decode_callback = fp;
self->video_decode_callback_user_data = user;
}
void plm_set_audio_decode_callback(plm_t *self, plm_audio_decode_callback fp, void *user) {
self->audio_decode_callback = fp;
self->audio_decode_callback_user_data = user;
}
int plm_decode(plm_t *self, double tick) {
DEBUGF("%s", "plm_decode");
int decode_video = (self->video_decode_callback && self->video_packet_type);
int decode_audio = (self->audio_decode_callback && self->audio_packet_type);
if (!decode_video && !decode_audio) {
// Nothing to do here
return false;
}
int did_decode = false;
int video_ended = false;
int audio_ended = false;
double video_target_time = self->time + tick;
double audio_target_time = self->time + tick;
if (self->audio_lead_time > 0 && decode_audio) {
video_target_time -= self->audio_lead_time;
}
else {
audio_target_time -= self->audio_lead_time;
}
do {
did_decode = false;
if (decode_video && plm_video_get_time(self->video_decoder) < video_target_time) {
plm_frame_t *frame = plm_video_decode(self->video_decoder);
if (frame) {
self->video_decode_callback(self, frame, self->video_decode_callback_user_data);
did_decode = true;
}
else {
video_ended = true;
}
}
if (decode_audio && plm_audio_get_time(self->audio_decoder) < audio_target_time) {
plm_samples_t *samples = plm_audio_decode(self->audio_decoder);
if (samples) {
self->audio_decode_callback(self, samples, self->audio_decode_callback_user_data);
did_decode = true;
}
else {
audio_ended = true;
}
}
} while (did_decode);
// We wanted to decode something but failed -> the source must have ended
if ((!decode_video || video_ended) && (!decode_audio || audio_ended)) {
plm_handle_end(self);
}
else {
self->time += tick;
}
return did_decode ? true : false;
}
plm_frame_t *plm_decode_video(plm_t *self) {
if (!self->video_packet_type) {
return NULL;
}
plm_frame_t *frame = plm_video_decode(self->video_decoder);
if (frame) {
self->time = frame->time;
}
else {
plm_handle_end(self);
}
return frame;
}
plm_samples_t *plm_decode_audio(plm_t *self) {
if (!self->audio_packet_type) {
return NULL;
}
plm_samples_t *samples = plm_audio_decode(self->audio_decoder);
if (samples) {
self->time = samples->time;
}
else {
plm_handle_end(self);
}
return samples;
}
void plm_handle_end(plm_t *self) {
if (self->loop) {
plm_rewind(self);
}
else {
self->has_ended = true;
}
}
void plm_read_video_packet(plm_buffer_t *buffer, void *user) {
plm_t *self = (plm_t *)user;
plm_read_packets(self, self->video_packet_type);
}
void plm_read_audio_packet(plm_buffer_t *buffer, void *user) {
plm_t *self = (plm_t *)user;
plm_read_packets(self, self->audio_packet_type);
}
void plm_read_packets(plm_t *self, int requested_type) {
plm_packet_t *packet;
while ((packet = plm_demux_decode(self->demux))) {
if (packet->type == self->video_packet_type) {
plm_buffer_write(self->video_buffer, packet->data, packet->length);
}
else if (packet->type == self->audio_packet_type) {
plm_buffer_write(self->audio_buffer, packet->data, packet->length);
}
if (packet->type == requested_type) {
return;
}
}
}

88
dsp/mpeg/slowrgb.c Normal file
View file

@ -0,0 +1,88 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
vi: set et ft=c ts=4 sw=4 fenc=utf-8 :vi
PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
Dominic Szablewski - https://phoboslab.org │
The MIT License(MIT)
Copyright(c) 2019 Dominic Szablewski
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 "dsp/mpeg/mpeg.h"
#include "libc/macros.h"
asm(".ident\t\"\\n\\n\
PL_MPEG (MIT License)\\n\
Copyright(c) 2019 Dominic Szablewski\\n\
https://phoboslab.org\"");
asm(".include \"libc/disclaimer.inc\"");
/**
* @see YCbCr2RGB() in tool/viz/lib/ycbcr2rgb.c
*/
void plm_frame_to_rgb(plm_frame_t *frame, uint8_t *rgb) {
// Chroma values are the same for each block of 4 pixels, so we proccess
// 2 lines at a time, 2 neighboring pixels each.
int w = frame->y.width, w2 = w >> 1;
int y_index1 = 0, y_index2 = w, y_next_2_lines = w + (w - frame->width);
int c_index = 0, c_next_line = w2 - (frame->width >> 1);
int rgb_index1 = 0, rgb_index2 = frame->width * 3,
rgb_next_2_lines = frame->width * 3;
int cols = frame->width >> 1, rows = frame->height >> 1;
int ccb, ccr, r, g, b;
uint8_t *y = frame->y.data, *cb = frame->cb.data, *cr = frame->cr.data;
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
ccb = cb[c_index];
ccr = cr[c_index];
c_index++;
r = (ccr + ((ccr * 103) >> 8)) - 179;
g = ((ccb * 88) >> 8) - 44 + ((ccr * 183) >> 8) - 91;
b = (ccb + ((ccb * 198) >> 8)) - 227;
// Line 1
int y1 = y[y_index1++];
int y2 = y[y_index1++];
rgb[rgb_index1 + 0] = MAX(0, MIN(255, y1 + r));
rgb[rgb_index1 + 1] = MAX(0, MIN(255, y1 - g));
rgb[rgb_index1 + 2] = MAX(0, MIN(255, y1 + b));
rgb[rgb_index1 + 3] = MAX(0, MIN(255, y2 + r));
rgb[rgb_index1 + 4] = MAX(0, MIN(255, y2 - g));
rgb[rgb_index1 + 5] = MAX(0, MIN(255, y2 + b));
rgb_index1 += 6;
// Line 2
int y3 = y[y_index2++];
int y4 = y[y_index2++];
rgb[rgb_index2 + 0] = MAX(0, MIN(255, y3 + r));
rgb[rgb_index2 + 1] = MAX(0, MIN(255, y3 - g));
rgb[rgb_index2 + 2] = MAX(0, MIN(255, y3 + b));
rgb[rgb_index2 + 3] = MAX(0, MIN(255, y4 + r));
rgb[rgb_index2 + 4] = MAX(0, MIN(255, y4 - g));
rgb[rgb_index2 + 5] = MAX(0, MIN(255, y4 + b));
rgb_index2 += 6;
}
y_index1 += y_next_2_lines;
y_index2 += y_next_2_lines;
rgb_index1 += rgb_next_2_lines;
rgb_index2 += rgb_next_2_lines;
c_index += c_next_line;
}
}

62
dsp/mpeg/video.h Normal file
View file

@ -0,0 +1,62 @@
#ifndef COSMOPOLITAN_DSP_MPEG_VIDEO_H_
#define COSMOPOLITAN_DSP_MPEG_VIDEO_H_
#include "dsp/mpeg/mpeg.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
typedef struct {
int full_px;
int is_set;
int r_size;
int h;
int v;
} plm_video_motion_t;
typedef struct plm_video_t {
double framerate;
double time;
double pixel_aspect_ratio;
int frames_decoded;
int width;
int height;
int mb_width;
int mb_height;
int mb_size;
int luma_width;
int luma_height;
int chroma_width;
int chroma_height;
int start_code;
int picture_type;
plm_video_motion_t motion_forward;
plm_video_motion_t motion_backward;
int has_sequence_header;
int quantizer_scale;
int slice_begin;
int macroblock_address;
int mb_row;
int mb_col;
int macroblock_type;
int macroblock_intra;
int dc_predictor[3];
plm_buffer_t *buffer;
int destroy_buffer_when_done;
plm_frame_t frame_current;
plm_frame_t frame_forward;
plm_frame_t frame_backward;
uint8_t *frames_data;
int block_data[64];
uint8_t intra_quant_matrix[64];
uint8_t non_intra_quant_matrix[64];
int has_reference_frame;
int assume_no_b_frames;
} plm_video_t;
void plm_video_process_macroblock_8(plm_video_t *, uint8_t *restrict,
uint8_t *restrict, int, int, bool);
void plm_video_process_macroblock_16(plm_video_t *, uint8_t *restrict,
uint8_t *restrict, int, int, bool);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_MPEG_VIDEO_H_ */

139
dsp/mpeg/ycbcrio.c Normal file
View file

@ -0,0 +1,139 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/mpeg/mpeg.h"
#include "dsp/mpeg/ycbcrio.h"
#include "libc/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
static void CheckPlmFrame(const struct plm_frame_t *frame) {
CHECK_NE(0, frame->width);
CHECK_NE(0, frame->height);
CHECK_GE(frame->y.width, frame->width);
CHECK_GE(frame->y.height, frame->height);
CHECK_EQ(frame->cr.width, frame->cb.width);
CHECK_EQ(frame->cr.height, frame->cb.height);
CHECK_EQ(frame->y.width, frame->cr.width * 2);
CHECK_EQ(frame->y.height, frame->cr.height * 2);
CHECK_NOTNULL(frame->y.data);
CHECK_NOTNULL(frame->cr.data);
CHECK_NOTNULL(frame->cb.data);
}
static size_t GetHeaderBytes(const struct plm_frame_t *frame) {
return MAX(sizeof(struct Ycbcrio), ROUNDUP(frame->y.width, 16));
}
static size_t GetPlaneBytes(const struct plm_plane_t *plane) {
/*
* planes must be 16-byte aligned, but due to their hugeness, and the
* recommendation of intel's 6,000 page manual, it makes sense to have
* planes on isolated 64kb frames for multiprocessing.
*/
return ROUNDUP(ROUNDUP(plane->height, 16) * ROUNDUP(plane->width, 16),
FRAMESIZE);
}
static size_t CalcMapBytes(const struct plm_frame_t *frame) {
return ROUNDUP(GetHeaderBytes(frame) + GetPlaneBytes(&frame->y) +
GetPlaneBytes(&frame->cb) + GetPlaneBytes(&frame->cb),
FRAMESIZE);
}
static void FixupPointers(struct Ycbcrio *map) {
map->frame.y.data = (unsigned char *)map + GetHeaderBytes(&map->frame);
map->frame.cr.data = map->frame.y.data + GetPlaneBytes(&map->frame.y);
map->frame.cb.data = map->frame.cr.data + GetPlaneBytes(&map->frame.cr);
}
static struct Ycbcrio *YcbcrioOpenNew(const char *path,
const struct plm_frame_t *frame) {
int fd;
size_t size;
struct stat st;
struct Ycbcrio *map;
CheckPlmFrame(frame);
size = CalcMapBytes(frame);
CHECK_NE(-1, (fd = open(path, O_CREAT | O_RDWR, 0644)));
CHECK_NE(-1, fstat(fd, &st));
if (st.st_size < size) {
CHECK_NE(-1, ftruncate(fd, size));
}
CHECK_NE(MAP_FAILED,
(map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)));
map->magic = YCBCRIO_MAGIC;
map->fd = fd;
map->size = size;
memcpy(&map->frame, frame, sizeof(map->frame));
FixupPointers(map);
memcpy(&map->frame.y.data, frame->y.data, GetPlaneBytes(&frame->y));
memcpy(&map->frame.cb.data, frame->cb.data, GetPlaneBytes(&frame->cb));
memcpy(&map->frame.cr.data, frame->cr.data, GetPlaneBytes(&frame->cr));
return map;
}
static struct Ycbcrio *YcbcrioOpenExisting(const char *path) {
int fd;
struct stat st;
struct Ycbcrio *map;
CHECK_NE(-1, (fd = open(path, O_RDWR)));
CHECK_NE(-1, fstat(fd, &st));
CHECK_NE(MAP_FAILED, (map = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0)));
CHECK_EQ(YCBCRIO_MAGIC, map->magic);
CHECK_GE(st.st_size, CalcMapBytes(&map->frame));
FixupPointers(map);
map->fd = fd;
map->size = st.st_size;
return map;
}
/**
* Opens shareable persistable MPEG video frame memory.
*
* @param path is a file name
* @param frame if NULL means open existing file, otherwise copies new
* @param points to pointer returned by YcbcrioOpen() which is cleared
* @return memory mapping needing YcbcrioClose()
*/
struct Ycbcrio *YcbcrioOpen(const char *path, const struct plm_frame_t *frame) {
if (frame) {
return YcbcrioOpenNew(path, frame);
} else {
return YcbcrioOpenExisting(path);
}
}
/**
* Closes mapped video frame file.
*
* @param points to pointer returned by YcbcrioOpen() which is cleared
*/
void YcbcrioClose(struct Ycbcrio **map) {
CHECK_NE(-1, close_s(&(*map)->fd));
CHECK_NE(-1, munmap_s(map, (*map)->size));
}

27
dsp/mpeg/ycbcrio.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef COSMOPOLITAN_DSP_MPEG_YCBCRIO_H_
#define COSMOPOLITAN_DSP_MPEG_YCBCRIO_H_
#include "dsp/mpeg/mpeg.h"
#include "libc/bits/bits.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define YCBCRIO_MAGIC bswap_32(0xBCCBCCBCu)
/**
* Mappable persistable MPEG-2 video frame in Y-Cr-Cb colorspace.
*/
struct Ycbcrio {
uint32_t magic;
int32_t fd;
uint64_t size;
plm_frame_t frame;
};
struct Ycbcrio *YcbcrioOpen(const char *, const struct plm_frame_t *)
paramsnonnull((1)) vallocesque returnsnonnull;
void YcbcrioClose(struct Ycbcrio **) paramsnonnull();
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_MPEG_YCBCRIO_H_ */

View file

@ -0,0 +1,100 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/scale/scale.h"
#include "libc/assert.h"
#include "libc/intrin/packuswb.h"
#include "libc/intrin/paddw.h"
#include "libc/intrin/palignr.h"
#include "libc/intrin/pmaddubsw.h"
#include "libc/intrin/psraw.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/str/str.h"
#define TAPS 8
#define RATIO 2
#define OFFSET 3
#define STRIDE 8
#define SPREAD (STRIDE * RATIO + TAPS - OFFSET)
#define OVERLAP (SPREAD - STRIDE * RATIO)
#define LOOKBEHIND OFFSET
#define LOOKAHEAD (SPREAD - LOOKBEHIND)
#define SCALE 5
#define ROUND (1 << (SCALE - 1))
/**
* Performs 2D Motion Picture Convolution Acceleration by Leveraging SSSE3.
*
* @note H/T John Costella, Jean-Baptiste Joseph Fourier
* @note RIP Huixiang Chen
*/
void *cDecimate2xUint8x8(unsigned long n, unsigned char A[n],
const signed char K[8]) {
short kRound[8] = {ROUND, ROUND, ROUND, ROUND, ROUND, ROUND, ROUND, ROUND};
signed char kMadd1[16] = {K[0], K[1], K[0], K[1], K[0], K[1], K[0], K[1],
K[0], K[1], K[0], K[1], K[0], K[1], K[0], K[1]};
signed char kMadd2[16] = {K[2], K[3], K[2], K[3], K[2], K[3], K[2], K[3],
K[2], K[3], K[2], K[3], K[2], K[3], K[2], K[3]};
signed char kMadd3[16] = {K[4], K[5], K[4], K[5], K[4], K[5], K[4], K[5],
K[4], K[5], K[4], K[5], K[4], K[5], K[4], K[5]};
signed char kMadd4[16] = {K[6], K[7], K[6], K[7], K[6], K[7], K[6], K[7],
K[6], K[7], K[6], K[7], K[6], K[7], K[6], K[7]};
unsigned char in1[16], in2[16], in3[16], in4[32];
unsigned char bv0[16], bv1[16], bv2[16], bv3[16];
short wv0[8], wv1[8], wv2[8], wv3[8];
unsigned long i, j, v, w, o;
if (n >= STRIDE) {
i = 0;
w = (n + RATIO / 2) / RATIO;
memset(in1, A[0], sizeof(in1));
memset(in2, A[n - 1], 16);
memcpy(in2, A, MIN(16, n));
for (; i < w; i += STRIDE) {
j = i * RATIO + 16;
if (j + 16 <= n) {
memcpy(in3, &A[j], 16);
} else {
memset(in3, A[n - 1], 16);
if (j < n) {
memcpy(in3, &A[j], n - j);
}
}
palignr(bv0, in2, in1, 13);
palignr(bv1, in2, in1, 15);
palignr(bv2, in3, in2, 1);
palignr(bv3, in3, in2, 3);
pmaddubsw(wv0, bv0, kMadd1);
pmaddubsw(wv1, bv1, kMadd2);
pmaddubsw(wv2, bv2, kMadd3);
pmaddubsw(wv3, bv3, kMadd4);
paddw(wv0, wv0, kRound);
paddw(wv0, wv0, wv1);
paddw(wv0, wv0, wv2);
paddw(wv0, wv0, wv3);
psraw(wv0, wv0, SCALE);
packuswb(bv2, wv0, wv0);
memcpy(&A[i], bv2, STRIDE);
memcpy(in1, in2, 16);
memcpy(in2, in3, 16);
}
}
return A;
}

285
dsp/scale/gyarados.c Normal file
View file

@ -0,0 +1,285 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/c161.h"
#include "dsp/core/core.h"
#include "dsp/core/ituround.h"
#include "dsp/core/q.h"
#include "dsp/core/twixt8.h"
#include "dsp/scale/scale.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
#include "third_party/dtoa/dtoa.h"
#include "tool/viz/lib/knobs.h"
/**
* @fileoverview Gyarados resizes graphics.
* @note H/T John Costella, Facebook, Photoshop, Carl Friedrich Gauss
* @note Eric Brasseur has an interesting blog post on tip of iceberg
* @see Magikarp
*/
#define M 14
#define SQR(X) ((X) * (X))
struct SamplingSolution {
int n, s;
void *weights;
void *indices;
};
static double ComputeWeight(double x) {
if (-1.5 < x && x < 1.5) {
if (-.5 < x && x < .5) {
return .75 - SQR(x);
} else if (x < 0) {
return .5 * SQR(x + 1.5);
} else {
return .5 * SQR(x - 1.5);
}
} else {
return 0;
}
}
static struct SamplingSolution *NewSamplingSolution(long n, long s) {
struct SamplingSolution *ss;
ss = xcalloc(1, sizeof(struct SamplingSolution));
ss->n = n;
ss->s = s;
ss->weights = xcalloc(n * s, sizeof(short));
ss->indices = xcalloc(n * s, sizeof(short));
return ss;
}
static bool IsNormalized(int n, double A[n]) {
int i;
double x;
for (x = i = 0; i < n; ++i) x += A[i];
return fabs(x - 1) < 1e-4;
}
void FreeSamplingSolution(struct SamplingSolution *ss) {
long i;
if (ss) {
free(ss->indices);
free(ss->weights);
free(ss);
}
}
struct SamplingSolution *ComputeSamplingSolution(long dn, long sn, double dar,
double off, double par) {
double *fweights;
double sum, hw, w, x, f;
short *weights, *indices;
struct SamplingSolution *res;
long j, i, k, n, min, max, s, N[6];
if (!dar) dar = sn, dar /= dn;
if (!off) off = (dar - 1) / 2;
f = dar < 1 ? 1 / dar : dar;
s = 3 * f + 1;
fweights = gc(xcalloc(s, sizeof(double)));
res = NewSamplingSolution(dn, s);
weights = res->weights;
indices = res->indices;
for (i = 0; i < dn; ++i) {
x = off + i * dar;
hw = 1.5 * f;
min = ceil(x - hw);
max = floor(x + hw);
n = max - min + 1;
CHECK_LE(n, s);
for (k = 0, j = min; j <= max; ++j) {
fweights[k++] = ComputeWeight((j - x) / (f / par));
}
for (sum = k = 0; k < n; ++k) sum += fweights[k];
for (j = 0; j < n; ++j) fweights[j] *= 1 / sum;
DCHECK(IsNormalized(n, fweights));
for (j = 0; j < n; ++j) {
indices[i * s + j] = MIN(sn - 1, MAX(0, min + j));
}
for (j = 0; j < n; j += 6) {
GetIntegerCoefficients(N, fweights + j, M, 0, 255);
for (k = 0; k < MIN(6, n - j); ++k) {
weights[i * s + j + k] = N[k];
}
}
}
return res;
}
static void *ZeroMatrix(long yw, long xw, int p[yw][xw], long yn, long xn) {
long y;
for (y = 0; y < yn; ++y) {
memset(p[y], 0, xn);
}
return p;
}
static int Sharpen(int ax, int bx, int cx) {
return (-1 * ax + 6 * bx + -1 * cx + 2) / 4;
}
static void GyaradosImpl(long dyw, long dxw, int dst[dyw][dxw], long syw,
long sxw, const int src[syw][sxw], long dyn, long dxn,
long syn, long sxn, int tmp0[restrict dyn][sxn],
int tmp1[restrict dyn][sxn],
int tmp2[restrict dyn][dxn], long yfn, long xfn,
const short fyi[dyn][yfn], const short fyw[dyn][yfn],
const short fxi[dxn][xfn], const short fxw[dxn][xfn],
bool sharpen) {
long i, j;
int eax, dy, dx, sy, sx;
for (sx = 0; sx < sxn; ++sx) {
for (dy = 0; dy < dyn; ++dy) {
for (eax = i = 0; i < yfn; ++i) {
eax += fyw[dy][i] * src[fyi[dy][i]][sx];
}
tmp0[dy][sx] = QRS(M, eax);
}
}
for (dy = 0; dy < dyn; ++dy) {
/* TODO: pmulhrsw() would probably make this much faster */
for (sx = 0; sx < sxn; ++sx) {
tmp1[dy][sx] = sharpen ? Sharpen(tmp0[MIN(dyn - 1, MAX(0, dy - 1))][sx],
tmp0[dy][sx],
tmp0[MIN(dyn - 1, MAX(0, dy + 1))][sx])
: tmp0[dy][sx];
}
}
for (dx = 0; dx < dxn; ++dx) {
for (dy = 0; dy < dyn; ++dy) {
for (eax = i = 0; i < xfn; ++i) {
eax += fxw[dx][i] * tmp1[dy][fxi[dx][i]];
}
tmp2[dy][dx] = QRS(M, eax);
}
}
for (dx = 0; dx < dxn; ++dx) {
for (dy = 0; dy < dyn; ++dy) {
dst[dy][dx] = sharpen ? Sharpen(tmp2[dy][MIN(dxn - 1, MAX(0, dx - 1))],
tmp2[dy][dx],
tmp2[dy][MIN(dxn - 1, MAX(0, dx + 1))])
: tmp2[dy][dx];
}
}
}
/**
* Scales image.
*
* @note gyarados is magikarp in its infinite form
* @see Magikarp2xY(), Magikarp2xX()
*/
void *Gyarados(long dyw, long dxw, int dst[dyw][dxw], long syw, long sxw,
const int src[syw][sxw], long dyn, long dxn, long syn, long sxn,
struct SamplingSolution *cy, struct SamplingSolution *cx,
bool sharpen) {
if (dyn > 0 && dxn > 0) {
if (syn > 0 && sxn > 0) {
CHECK_LE(syn, syw);
CHECK_LE(sxn, sxw);
CHECK_LE(dyn, dyw);
CHECK_LE(dxn, dxw);
CHECK_LT(bsrl(syn) + bsrl(sxn), 32);
CHECK_LT(bsrl(dyn) + bsrl(dxn), 32);
CHECK_LE(dyw, 0x7fff);
CHECK_LE(dxw, 0x7fff);
CHECK_LE(syw, 0x7fff);
CHECK_LE(sxw, 0x7fff);
CHECK_LE(dyn, 0x7fff);
CHECK_LE(dxn, 0x7fff);
CHECK_LE(syn, 0x7fff);
CHECK_LE(sxn, 0x7fff);
GyaradosImpl(dyw, dxw, dst, syw, sxw, src, dyn, dxn, syn, sxn,
gc(xmemalign(64, sizeof(int) * dyn * sxn)),
gc(xmemalign(64, sizeof(int) * dyn * sxn)),
gc(xmemalign(64, sizeof(int) * dyn * dxn)), cy->s, cx->s,
cy->indices, cy->weights, cx->indices, cx->weights, sharpen);
} else {
ZeroMatrix(dyw, dxw, dst, dyn, dxn);
}
}
return dst;
}
void *GyaradosUint8(long dyw, long dxw, unsigned char dst[dyw][dxw], long syw,
long sxw, const unsigned char src[syw][sxw], long dyn,
long dxn, long syn, long sxn, long lo, long hi,
struct SamplingSolution *cy, struct SamplingSolution *cx,
bool sharpen) {
static bool once;
static int Tin[256];
static unsigned char Tout[32768];
long i, y, x;
int(*tmp)[MAX(dyn, syn)][MAX(dxn, sxn)];
if (!once) {
for (i = 0; i < ARRAYLEN(Tin); ++i) {
Tin[i] = F2Q(15, rgb2linpc(i / 255., 2.4));
}
for (i = 0; i < ARRAYLEN(Tout); ++i) {
Tout[i] = MIN(255, MAX(0, round(rgb2stdpc(Q2F(15, i), 2.4) * 255.)));
}
once = true;
}
tmp = xmemalign(64, sizeof(int) * MAX(dyn, syn) * MAX(dxn, sxn));
for (y = 0; y < syn; ++y) {
for (x = 0; x < sxn; ++x) {
(*tmp)[y][x] = Tin[src[y][x]];
}
}
Gyarados(MAX(dyn, syn), MAX(dxn, sxn), *tmp, MAX(dyn, syn), MAX(dxn, sxn),
*tmp, dyn, dxn, syn, sxn, cy, cx, sharpen);
for (y = 0; y < dyn; ++y) {
for (x = 0; x < dxn; ++x) {
dst[y][x] = Tout[MIN(32767, MAX(0, (*tmp)[y][x]))];
}
}
free(tmp);
return dst;
}
void *EzGyarados(long dcw, long dyw, long dxw, unsigned char dst[dcw][dyw][dxw],
long scw, long syw, long sxw,
const unsigned char src[scw][syw][sxw], long c0, long cn,
long dyn, long dxn, long syn, long sxn, double ry, double rx,
double oy, double ox) {
long c;
struct SamplingSolution *cy, *cx;
cy = ComputeSamplingSolution(dyn, syn, ry, oy, 1);
cx = ComputeSamplingSolution(dxn, sxn, rx, ox, 1);
for (c = c0; c < cn; ++c) {
GyaradosUint8(dyw, dxw, dst[c], syw, sxw, src[c], dyn, dxn, syn, sxn, 0,
255, cy, cx, true);
}
FreeSamplingSolution(cx);
FreeSamplingSolution(cy);
return dst;
}

129
dsp/scale/magikarp.c Normal file
View file

@ -0,0 +1,129 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/half.h"
#include "dsp/core/ks8.h"
#include "dsp/core/kss8.h"
#include "dsp/scale/scale.h"
#include "libc/macros.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
/**
* @fileoverview Magikarp resizes graphics in half very fast.
* @note H/T John Costella, Facebook, and Photoshop
* @note sharpening is good for luma but not chroma
* @see Gyarados
*/
#define CLAMP(X) MIN(255, MAX(0, X))
#define MAGIKARP(...) \
CLAMP(KS8(5, K[0], K[1], K[2], K[3], K[4], K[5], K[6], K[7], __VA_ARGS__))
signed char g_magikarp[8];
const signed char kMagikarp[8][8] = {
{-1, -3, 3, 17, 17, 3, -3, -1}, /* 1331+161 derived w/ one off cas */
{-1, -3, 6, 28, 6, -3, -1, 0}, /* due to the convolution theorem? */
{0, 0, -11, 53, -11, 0, 0, 0}, /* plus, some random experimenting */
{-2, -6, 2, 22, 22, 2, -6, -2}, /* one a line please clang-format? */
{-3, -9, 1, 27, 27, 1, -9, -3},
};
signed char g_magkern[8];
const signed char kMagkern[8][8] = {
{1, 2, 3, 10, 10, 3, 2, 1},
{0, 4, 4, 16, 4, 4, 0, 0},
{0, 1, 2, 6, 14, 6, 2, 1},
{0, 1, 2, 13, 13, 2, 1, 0},
};
void *Magikarp2xX(long ys, long xs, unsigned char p[ys][xs], long yn, long xn) {
long y;
if (yn && xn > 1) {
for (y = 0; y < yn; ++y) {
/* gcc/clang both struggle with left-to-right matrix ops */
cDecimate2xUint8x8(xn, p[y], g_magikarp);
}
}
return p;
}
void *Magikarp2xY(long ys, long xs, unsigned char p[ys][xs], long yn, long xn) {
long y, x, h;
signed char K[8];
memcpy(K, g_magikarp, sizeof(K));
for (h = HALF(yn), y = 0; y < h; ++y) {
for (x = 0; x < xn; ++x) {
p[y][x] = /* gcc/clang are good at optimizing top-to-bottom matrix ops */
MAGIKARP(p[MAX(00 + 0, y * 2 - 3)][x], p[MAX(00 + 0, y * 2 - 2)][x],
p[MAX(00 + 0, y * 2 - 1)][x], p[MIN(yn - 1, y * 2 + 0)][x],
p[MIN(yn - 1, y * 2 + 1)][x], p[MIN(yn - 1, y * 2 + 2)][x],
p[MIN(yn - 1, y * 2 + 3)][x], p[MIN(yn - 1, y * 2 + 4)][x]);
}
}
return p;
}
void *Magkern2xX(long ys, long xs, unsigned char p[ys][xs], long yn, long xn) {
long y;
if (yn && xn > 1) {
for (y = 0; y < yn; ++y) {
cDecimate2xUint8x8(xn, p[y], g_magkern);
}
}
return p;
}
void *Magkern2xY(long ys, long xs, unsigned char p[ys][xs], long yn, long xn) {
long y, x, h;
signed char K[8];
memcpy(K, g_magkern, sizeof(K));
for (h = HALF(yn), y = 0; y < h; ++y) {
for (x = 0; x < xn; ++x) {
p[y][x] =
MAGIKARP(p[MAX(00 + 0, y * 2 - 3)][x], p[MAX(00 + 0, y * 2 - 2)][x],
p[MAX(00 + 0, y * 2 - 1)][x], p[MIN(yn - 1, y * 2 + 0)][x],
p[MIN(yn - 1, y * 2 + 1)][x], p[MIN(yn - 1, y * 2 + 2)][x],
p[MIN(yn - 1, y * 2 + 3)][x], p[MIN(yn - 1, y * 2 + 4)][x]);
}
}
return p;
}
void *MagikarpY(long dys, long dxs, unsigned char d[restrict dys][dxs],
long sys, long sxs, const unsigned char s[sys][sxs], long yn,
long xn, const signed char K[8]) {
long y, x;
for (y = 0; y < yn; ++y) {
for (x = 0; x < xn; ++x) {
d[y][x] = MAGIKARP(s[MAX(00 + 0, y - 3)][x], s[MAX(00 + 0, y - 2)][x],
s[MAX(00 + 0, y - 1)][x], s[MIN(yn - 1, y + 0)][x],
s[MIN(yn - 1, y + 1)][x], s[MIN(yn - 1, y + 2)][x],
s[MIN(yn - 1, y + 3)][x], s[MIN(yn - 1, y + 4)][x]);
}
}
return d;
}
static textstartup void g_magikarp_init() {
memcpy(g_magkern, kMagkern[0], sizeof(g_magkern));
memcpy(g_magikarp, kMagikarp[0], sizeof(g_magikarp));
}
const void *const g_magikarp_ctor[] initarray = {g_magikarp_init};

41
dsp/scale/scale.c Normal file
View file

@ -0,0 +1,41 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/half.h"
#include "dsp/scale/scale.h"
void *Scale2xX(long ys, long xs, unsigned char p[ys][xs], long yn, long xn) {
long y, x, w;
for (w = HALF(xn), y = 0; y < yn; ++y) {
for (x = 0; x < w; ++x) {
p[y][x] = p[y][x * 2];
}
}
return p;
}
void *Scale2xY(long ys, long xs, unsigned char p[ys][xs], long yn, long xn) {
long y, x, h;
for (h = HALF(yn), y = 0; y < h; ++y) {
for (x = 0; x < xn; ++x) {
p[y][x] = p[y * 2][x];
}
}
return p;
}

49
dsp/scale/scale.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef COSMOPOLITAN_DSP_SCALE_SCALE_H_
#define COSMOPOLITAN_DSP_SCALE_SCALE_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern long gyarados_latency_;
extern signed char g_magikarp[8];
extern signed char g_magkern[8];
extern const signed char kMagikarp[8][8];
extern const signed char kMagkern[8][8];
struct SamplingSolution;
void FreeSamplingSolution(struct SamplingSolution *);
struct SamplingSolution *ComputeSamplingSolution(long, long, double, double,
double);
void *Scale2xX(long ys, long xs, unsigned char[ys][xs], long, long);
void *Scale2xY(long ys, long xs, unsigned char[ys][xs], long, long);
void *Magikarp2xX(long ys, long xs, unsigned char[ys][xs], long, long);
void *Magikarp2xY(long ys, long xs, unsigned char[ys][xs], long, long);
void *Magkern2xX(long ys, long xs, unsigned char[ys][xs], long, long);
void *Magkern2xY(long ys, long xs, unsigned char[ys][xs], long, long);
void *MagikarpY(long dys, long dxs, unsigned char d[restrict dys][dxs],
long sys, long sxs, const unsigned char s[sys][sxs], long yn,
long xn, const signed char K[8]);
void *GyaradosUint8(long dyw, long dxw, unsigned char dst[dyw][dxw], long syw,
long sxw, const unsigned char src[syw][sxw], long dyn,
long dxn, long syn, long sxn, long lo, long hi,
struct SamplingSolution *cy, struct SamplingSolution *cx,
bool sharpen);
void *EzGyarados(long dcw, long dyw, long dxw, unsigned char dst[dcw][dyw][dxw],
long scw, long syw, long sxw,
const unsigned char src[scw][syw][sxw], long c0, long cn,
long dyn, long dxn, long syn, long sxn, double ry, double rx,
double oy, double ox);
void Decimate2xUint8x8(unsigned long n, unsigned char[n * 2],
const signed char[static 8]);
void *cDecimate2xUint8x8(unsigned long n, unsigned char[n * 2],
const signed char[8]);
void *transpose(long yn, long xn, const unsigned char[yn][xn]);
extern void (*const transpose88b)(unsigned char[8][8]);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_SCALE_SCALE_H_ */

58
dsp/scale/scale.mk Normal file
View file

@ -0,0 +1,58 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += DSP_SCALE
DSP_SCALE_ARTIFACTS += DSP_SCALE_A
DSP_SCALE = $(DSP_SCALE_A_DEPS) $(DSP_SCALE_A)
DSP_SCALE_A = o/$(MODE)/dsp/scale/scale.a
DSP_SCALE_A_FILES := $(wildcard dsp/scale/*)
DSP_SCALE_A_HDRS = $(filter %.h,$(DSP_SCALE_A_FILES))
DSP_SCALE_A_SRCS_S = $(filter %.S,$(DSP_SCALE_A_FILES))
DSP_SCALE_A_SRCS_C = $(filter %.c,$(DSP_SCALE_A_FILES))
DSP_SCALE_A_SRCS = \
$(DSP_SCALE_A_SRCS_S) \
$(DSP_SCALE_A_SRCS_C)
DSP_SCALE_A_OBJS = \
$(DSP_SCALE_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(DSP_SCALE_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(DSP_SCALE_A_SRCS_C:%.c=o/$(MODE)/%.o)
DSP_SCALE_A_CHECKS = \
$(DSP_SCALE_A).pkg \
$(DSP_SCALE_A_HDRS:%=o/$(MODE)/%.ok)
DSP_SCALE_A_DIRECTDEPS = \
DSP_CORE \
LIBC_INTRIN \
LIBC_NEXGEN32E \
LIBC_TINYMATH \
LIBC_TIME \
LIBC_RUNTIME \
LIBC_LOG \
LIBC_MEM \
LIBC_X \
LIBC_STUBS
DSP_SCALE_A_DEPS := \
$(call uniq,$(foreach x,$(DSP_SCALE_A_DIRECTDEPS),$($(x))))
$(DSP_SCALE_A): dsp/scale/ \
$(DSP_SCALE_A).pkg \
$(DSP_SCALE_A_OBJS)
$(DSP_SCALE_A).pkg: \
$(DSP_SCALE_A_OBJS) \
$(foreach x,$(DSP_SCALE_A_DIRECTDEPS),$($(x)_A).pkg)
DSP_SCALE_LIBS = $(foreach x,$(DSP_SCALE_ARTIFACTS),$($(x)))
DSP_SCALE_SRCS = $(foreach x,$(DSP_SCALE_ARTIFACTS),$($(x)_SRCS))
DSP_SCALE_HDRS = $(foreach x,$(DSP_SCALE_ARTIFACTS),$($(x)_HDRS))
DSP_SCALE_CHECKS = $(foreach x,$(DSP_SCALE_ARTIFACTS),$($(x)_CHECKS))
DSP_SCALE_OBJS = $(foreach x,$(DSP_SCALE_ARTIFACTS),$($(x)_OBJS))
$(DSP_SCALE_OBJS): $(BUILD_FILES) dsp/scale/scale.mk
.PHONY: o/$(MODE)/dsp/scale
o/$(MODE)/dsp/scale: $(DSP_SCALE_CHECKS)

33
dsp/tty/altbuf.c Normal file
View file

@ -0,0 +1,33 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
/**
* Asks teletypewriter to flip to alternate display page.
*
* The alternate buffer trick lets one restore the console exactly as it
* was, once the program is done running.
*/
int ttyenablealtbuf(int ttyfd) { return ttysend(ttyfd, "\e[?1049h"); }
/**
* Asks teletypewriter to restore blinking box thing.
*/
int ttydisablealtbuf(int ttyfd) { return ttysend(ttyfd, "\e[?1049l"); }

46
dsp/tty/config.c Normal file
View file

@ -0,0 +1,46 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/assert.h"
#include "libc/calls/termios.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/termios.h"
/**
* Applies configuration to teletypewriter.
*
* @param opt_out_oldconf is only modified if successful
* @return 0 on success, or -1 w/ errno
* @see ttyconfig(), ttyrestore()
*/
int ttyconfig(int ttyfd, ttyconf_f fn, int64_t arg,
const struct termios *opt_out_oldconf) {
struct termios conf[2];
if (tcgetattr(ttyfd, &conf[0]) != -1 &&
fn(memcpy(&conf[1], &conf[0], sizeof(conf[0])), arg) != -1 &&
tcsetattr(ttyfd, TCSAFLUSH, &conf[1]) != -1) {
if (opt_out_oldconf) {
memcpy(opt_out_oldconf, &conf[0], sizeof(conf[0]));
}
return 0;
} else {
return -1;
}
}

77
dsp/tty/describe.c Normal file
View file

@ -0,0 +1,77 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/fmt/fmt.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
#define BUFFY 48
static char *ttydescriber(char *b, const struct TtyIdent *ti) {
switch (ti->id) {
case 0:
snprintf(b, BUFFY, "%s %d", "putty", ti->version);
break;
case 1:
if (ti->version > 1000) {
snprintf(b, BUFFY, "%s %d", "gnome terminal", ti->version);
} else {
snprintf(b, BUFFY, "%s %d", "mlterm", ti->version);
}
break;
case kTtyIdScreen:
snprintf(b, BUFFY, "%s %d", "gnu screen", ti->version);
break;
case 77:
snprintf(b, BUFFY, "%s %d", "redhat mintty", ti->version);
break;
case 41:
snprintf(b, BUFFY, "%s %d", "xterm", ti->version);
break;
case 82:
snprintf(b, BUFFY, "%s %d", "rxvt", ti->version);
break;
default:
snprintf(b, BUFFY, "%s %d %d", "unknown teletypewriter no.", ti->id,
ti->version);
break;
}
return b;
}
/**
* Makes educated guess about name of teletypewriter.
*/
char *ttydescribe(char *out, size_t size, const struct TtyIdent *ti) {
char b1[BUFFY], b2[BUFFY];
if (ti) {
ttydescriber(b1, ti);
if (ti->next) {
snprintf(out, size, "%s%s%s", ttydescriber(b2, ti->next), " inside ", b1);
} else {
snprintf(out, size, "%s", b1);
}
} else {
snprintf(out, size, "%s", "no tty");
}
return out;
}

53
dsp/tty/hidecursor.c Normal file
View file

@ -0,0 +1,53 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/bits/pushpop.h"
#include "libc/dce.h"
#include "libc/log/log.h"
#include "libc/nt/console.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/consolecursorinfo.h"
static int ttysetcursor(int fd, bool visible) {
struct NtConsoleCursorInfo ntcursor;
char code[8] = "\e[?25l";
if (isterminalinarticulate()) return 0;
if (visible) code[5] = 'h';
if (SupportsWindows()) {
GetConsoleCursorInfo(GetStdHandle(kNtStdOutputHandle), &ntcursor);
ntcursor.bVisible = visible;
SetConsoleCursorInfo(GetStdHandle(kNtStdOutputHandle), &ntcursor);
}
return ttysend(fd, code);
}
/**
* Asks teletypewriter to hide blinking box.
*/
int ttyhidecursor(int fd) {
return ttysetcursor(fd, false);
}
/**
* Asks teletypewriter to restore blinking box.
*/
int ttyshowcursor(int fd) {
return ttysetcursor(fd, true);
}

95
dsp/tty/ident.c Normal file
View file

@ -0,0 +1,95 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/termios.h"
#include "libc/fmt/fmt.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/errfuns.h"
static int ttyident_probe(struct TtyIdent *ti, int ttyinfd, int ttyoutfd,
const char *msg) {
ssize_t rc;
size_t got;
char buf[64];
int id, version;
if ((rc = write(ttyoutfd, msg, strlen(msg))) != -1) {
TryAgain:
if ((rc = read(ttyinfd, buf, sizeof(buf))) != -1) {
buf[min((got = (size_t)rc), sizeof(buf) - 1)] = '\0';
if (sscanf(buf, "\e[>%d;%d", &id, &version) >= 1) {
ti->id = id;
ti->version = version;
rc = 0;
} else {
rc = eio();
}
} else if (errno == EINTR) {
goto TryAgain;
} else if (errno == EAGAIN) {
if (poll((struct pollfd[]){{ttyinfd, POLLIN}}, 1, 100) != 0) {
goto TryAgain;
} else {
rc = etimedout();
}
}
}
return rc;
}
/**
* Identifies teletypewriter.
*
* For example, we can tell if process is running in a GNU Screen
* session Gnome Terminal.
*
* @return object if TTY responds, or NULL w/ errno
* @see ttyidentclear()
*/
int ttyident(struct TtyIdent *ti, int ttyinfd, int ttyoutfd) {
int rc;
struct termios old;
struct TtyIdent outer;
rc = -1;
if (!IsWindows()) {
if (ttyconfig(ttyinfd, ttysetrawdeadline, 3, &old) != -1) {
if (ttyident_probe(ti, ttyinfd, ttyoutfd, "\e[>c") != -1) {
rc = 0;
memset(&outer, 0, sizeof(outer));
if (ti->id == 83 /* GNU Screen */ && (ti->next || weaken(malloc)) &&
ttyident_probe(&outer, ttyinfd, ttyoutfd, "\eP\e[>c\e\\") != -1 &&
(ti->next = (ti->next ? ti->next
: weaken(malloc)(sizeof(struct TtyIdent))))) {
memcpy(ti->next, &outer, sizeof(outer));
} else {
free_s(&ti->next);
}
}
ttyrestore(ttyinfd, &old);
}
}
return rc;
}

43
dsp/tty/identclear.c Normal file
View file

@ -0,0 +1,43 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/assert.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
static void ttyidentfree(struct TtyIdent *ti) {
if (ti) {
assert(ti != ti->next);
ttyidentfree(ti->next);
free_s(&ti);
}
}
/**
* Destroys TtyIdent object.
*
* @see ttyident()
*/
void ttyidentclear(struct TtyIdent *ti) {
assert(ti != ti->next);
ttyidentfree(ti->next);
memset(ti, 0, sizeof(*ti));
}

33
dsp/tty/internal.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef COSMOPOLITAN_DSP_TTY_INTERNAL_H_
#define COSMOPOLITAN_DSP_TTY_INTERNAL_H_
#include "dsp/tty/ttyrgb.h"
#include "libc/bits/xmmintrin.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct TtyRgb rgb2tty24f_(__m128);
struct TtyRgb rgb2ttyf2i_(__m128);
struct TtyRgb rgb2ttyi2f_(int, int, int);
struct TtyRgb rgb2ansi_(int, int, int);
struct TtyRgb rgb2ansihash_(int, int, int);
struct TtyRgb rgb2xterm24_(int, int, int);
struct TtyRgb rgb2xterm256gray_(__m128);
struct TtyRgb tty2rgb_(struct TtyRgb);
struct TtyRgb tty2rgb24_(struct TtyRgb);
__m128 tty2rgbf_(struct TtyRgb);
__m128 tty2rgbf24_(struct TtyRgb);
char *setbg16_(char *, struct TtyRgb);
char *setfg16_(char *, struct TtyRgb);
char *setbgfg16_(char *, struct TtyRgb, struct TtyRgb);
char *setbg256_(char *, struct TtyRgb);
char *setfg256_(char *, struct TtyRgb);
char *setbgfg256_(char *, struct TtyRgb, struct TtyRgb);
char *setbg24_(char *, struct TtyRgb);
char *setfg24_(char *, struct TtyRgb);
char *setbgfg24_(char *, struct TtyRgb, struct TtyRgb);
struct TtyRgb rgb2ansi8_(int, int, int);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_TTY_INTERNAL_H_ */

51
dsp/tty/itoa8.c Normal file
View file

@ -0,0 +1,51 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/itoa8.h"
#include "libc/bits/bits.h"
#include "libc/str/str.h"
struct Itoa8 kItoa8;
static nooptimize textstartup void itoa8init(void) {
size_t i;
uint8_t z;
char p[4];
/*102*/
for (i = 0; i < 256; ++i) {
memset(p, 0, sizeof(p));
if (i < 10) {
z = 1;
p[0] = '0' + i;
} else if (i < 100) {
z = 2;
p[0] = '0' + i / 10;
p[1] = '0' + i % 10;
} else {
z = 3;
p[0] = '0' + i / 100;
p[1] = '0' + i % 100 / 10;
p[2] = '0' + i % 100 % 10;
}
kItoa8.size[i] = z;
memcpy(&kItoa8.data[i], p, sizeof(p));
}
}
INITIALIZER(301, _init_itoa8, itoa8init());

21
dsp/tty/itoa8.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef COSMOPOLITAN_DSP_TTY_ITOA8_H_
#define COSMOPOLITAN_DSP_TTY_ITOA8_H_
#include "libc/str/str.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct Itoa8 {
uint8_t size[256];
uint32_t data[256];
};
extern struct Itoa8 kItoa8;
forceinline char *itoa8(char *p, uint8_t c) {
memcpy(p, &kItoa8.data[c], 4);
return p + kItoa8.size[c];
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_TTY_ITOA8_H_ */

29
dsp/tty/kcgapalette.c Normal file
View file

@ -0,0 +1,29 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "dsp/tty/quant.h"
const ttypalette_t kCgaPalette = {
[0][0] = {0, 0, 0, 0}, /* normal black: \e[30m (or \e[38;5;0m) */
[1][0] = {85, 85, 85, 8}, /* bright black: \e[90m (or \e[38;5;8m) */
[0][1] = {170, 0, 0, 1}, /* normal red: \e[31m */
[1][1] = {255, 85, 85, 9}, /* bright red: \e[91m (or \e[1;31m) */
[0][2] = {0, 170, 0, 2}, /* normal green: \e[32m */
[1][2] = {85, 255, 85, 10}, /* bright green: \e[92m */
[0][3] = {170, 85, 0, 3}, /* normal yellow: \e[33m */
[1][3] = {255, 255, 85, 11}, /* bright yellow: \e[93m */
[0][4] = {0, 0, 170, 4}, /* normal blue: \e[34m */
[1][4] = {85, 85, 255, 12}, /* bright blue: \e[94m */
[0][5] = {170, 0, 170, 5}, /* normal magenta: \e[35m */
[1][5] = {255, 85, 255, 13}, /* bright magenta: \e[95m */
[0][6] = {0, 170, 170, 6}, /* normal cyan: \e[36m */
[1][6] = {85, 255, 255, 14}, /* bright cyan: \e[96m */
[0][7] = {170, 170, 170, 7}, /* normal white: \e[37m */
[1][7] = {255, 255, 255, 15}, /* bright white: \e[97m */
};

29
dsp/tty/ktangopalette.c Normal file
View file

@ -0,0 +1,29 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "dsp/tty/quant.h"
const ttypalette_t kTangoPalette = {
[0][0] = {0x2E, 0x34, 0x36, 0}, /* aluminium1 */
[1][0] = {0x55, 0x57, 0x53, 8},
[0][1] = {0xCC, 0x00, 0x00, 1}, /* scarietred */
[1][1] = {0xEF, 0x29, 0x29, 9},
[0][2] = {0x4E, 0x9A, 0x06, 2}, /* chameleon */
[1][2] = {0x8A, 0xE2, 0x34, 10},
[0][3] = {0xC4, 0xA0, 0x00, 3}, /* butter */
[1][3] = {0xFC, 0xE9, 0x4F, 11},
[0][4] = {0x34, 0x65, 0xA4, 4}, /* skyblue */
[1][4] = {0x72, 0x9F, 0xCF, 12},
[0][5] = {0x75, 0x50, 0x7B, 5}, /* plum */
[1][5] = {0xAD, 0x7F, 0xA8, 13},
[0][6] = {0x06, 0x98, 0x9A, 6}, /* cyan */
[1][6] = {0x34, 0xE2, 0xE2, 14},
[0][7] = {0xD3, 0xD7, 0xCF, 7}, /* aluminium2 */
[1][7] = {0xEE, 0xEE, 0xEC, 15},
};

22
dsp/tty/kxtermcubesteps.c Normal file
View file

@ -0,0 +1,22 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/quant.h"
const uint8_t kXtermCubeSteps[] = {0, 0137, 0207, 0257, 0327, 0377};

29
dsp/tty/kxtermpalette.c Normal file
View file

@ -0,0 +1,29 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "dsp/tty/quant.h"
const ttypalette_t kXtermPalette = {
[0][0] = {0, 0, 0, 0}, /* normal black: \e[30m (or \e[38;5;0m) */
[1][0] = {127, 127, 127, 8}, /* bright black: \e[90m (or \e[38;5;8m) */
[0][1] = {205, 0, 0, 1}, /* normal red: \e[31m */
[1][1] = {255, 0, 0, 9}, /* bright red: \e[91m (or \e[1;31m) */
[0][2] = {0, 205, 0, 2}, /* normal green: \e[32m */
[1][2] = {0, 255, 0, 10}, /* bright green: \e[92m */
[0][3] = {205, 205, 0, 3}, /* normal yellow: \e[33m */
[1][3] = {255, 255, 0, 11}, /* bright yellow: \e[93m */
[0][4] = {0, 0, 238, 4}, /* normal blue: \e[34m */
[1][4] = {92, 92, 255, 12}, /* bright blue: \e[94m */
[0][5] = {205, 0, 205, 5}, /* normal magenta: \e[35m */
[1][5] = {255, 0, 255, 13}, /* bright magenta: \e[95m */
[0][6] = {0, 205, 205, 6}, /* normal cyan: \e[36m */
[1][6] = {0, 255, 255, 14}, /* bright cyan: \e[96m */
[0][7] = {229, 229, 229, 7}, /* normal white: \e[37m */
[1][7] = {255, 255, 255, 15}, /* bright white: \e[97m */
};

29
dsp/tty/mpsadbw.S Normal file
View file

@ -0,0 +1,29 @@
/*-*- mode:asm; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify │
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License. │
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of │
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software │
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/macros.h"
/ TODO(jart): write me
movdqa a,%xmm0
mpsadbw $0,inv,%xmm0
.rodata.cst32
a: .byte 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
inv: .byte 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0

84
dsp/tty/quant.h Normal file
View file

@ -0,0 +1,84 @@
#ifndef DSP_TTY_QUANT_H_
#define DSP_TTY_QUANT_H_
#include "dsp/tty/ttyrgb.h"
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/xmmintrin.h"
#include "libc/limits.h"
#include "libc/str/str.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define TL 0
#define TR 1
#define BL 2
#define BR 3
typedef __m128 (*tty2rgbf_f)(struct TtyRgb);
typedef char *(*setbg_f)(char *, struct TtyRgb);
typedef char *(*setbgfg_f)(char *, struct TtyRgb, struct TtyRgb);
typedef char *(*setfg_f)(char *, struct TtyRgb);
typedef struct TtyRgb (*rgb2tty_f)(int, int, int);
typedef struct TtyRgb (*rgb2ttyf_f)(__m128);
typedef struct TtyRgb (*tty2rgb_f)(struct TtyRgb);
typedef struct TtyRgb ttypalette_t[2][8];
struct TtyQuant {
enum TtyQuantizationAlgorithm {
kTtyQuantAnsi,
kTtyQuantTrue,
kTtyQuantXterm256,
} alg;
enum TtyBlocksSelection {
kTtyBlocksUnicode,
kTtyBlocksCp437,
} blocks;
enum TtyQuantizationChannels {
kTtyQuantGrayscale = 1,
kTtyQuantRgb = 3,
} chans;
unsigned min;
unsigned max;
setbg_f setbg;
setfg_f setfg;
setbgfg_f setbgfg;
rgb2tty_f rgb2tty;
rgb2ttyf_f rgb2ttyf;
tty2rgb_f tty2rgb;
tty2rgbf_f tty2rgbf;
const ttypalette_t *palette;
};
extern const ttypalette_t kCgaPalette;
extern const ttypalette_t kXtermPalette;
extern const ttypalette_t kTangoPalette;
extern const uint8_t kXtermCubeSteps[6];
extern double g_xterm256_gamma;
extern struct TtyRgb g_ansi2rgb_[256];
extern struct TtyQuant g_ttyquant_;
extern const uint8_t kXtermXlat[2][256];
void ttyquantinit(enum TtyQuantizationAlgorithm, enum TtyQuantizationChannels,
enum TtyBlocksSelection);
extern char *ttyraster(char *, const struct TtyRgb *, size_t, size_t,
struct TtyRgb, struct TtyRgb);
#define ttyquant() (&g_ttyquant_)
#define TTYQUANT() VEIL("r", &g_ttyquant_)
#define rgb2tty(...) (ttyquant()->rgb2tty(__VA_ARGS__))
#define tty2rgb(...) (ttyquant()->tty2rgb(__VA_ARGS__))
#define rgb2ttyf(...) (ttyquant()->rgb2ttyf(__VA_ARGS__))
#define tty2rgbf(...) (ttyquant()->tty2rgbf(__VA_ARGS__))
#define setbg(...) (ttyquant()->setbg(__VA_ARGS__))
#define setfg(...) (ttyquant()->setfg(__VA_ARGS__))
#define setbgfg(...) (ttyquant()->setbgfg(__VA_ARGS__))
forceinline bool ttyeq(struct TtyRgb x, struct TtyRgb y) {
return x.r == y.r && x.g == y.g && x.b == y.b;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* DSP_TTY_QUANT_H_ */

78
dsp/tty/quantinit.c Normal file
View file

@ -0,0 +1,78 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/internal.h"
#include "dsp/tty/quant.h"
#include "libc/dce.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
struct TtyQuant g_ttyquant_;
/**
* Chooses xterm quantization mode.
*/
optimizesize textstartup void ttyquantinit(enum TtyQuantizationAlgorithm alg,
enum TtyQuantizationChannels chans,
enum TtyBlocksSelection blocks) {
switch (alg) {
case kTtyQuantAnsi:
TTYQUANT()->rgb2tty = rgb2ansi_;
TTYQUANT()->rgb2ttyf = rgb2ttyf2i_;
TTYQUANT()->tty2rgb = tty2rgb_;
TTYQUANT()->tty2rgbf = tty2rgbf_;
TTYQUANT()->setbg = setbg16_;
TTYQUANT()->setfg = setfg16_;
TTYQUANT()->setbgfg = setbgfg16_;
TTYQUANT()->min = 0;
TTYQUANT()->max = 16;
break;
case kTtyQuantTrue:
TTYQUANT()->rgb2tty = rgb2xterm24_;
TTYQUANT()->rgb2ttyf = rgb2tty24f_;
TTYQUANT()->tty2rgb = tty2rgb24_;
TTYQUANT()->tty2rgbf = tty2rgbf24_;
TTYQUANT()->setbg = setbg24_;
TTYQUANT()->setfg = setfg24_;
TTYQUANT()->setbgfg = setbgfg24_;
TTYQUANT()->min = 16;
TTYQUANT()->max = 256;
break;
case kTtyQuantXterm256:
TTYQUANT()->rgb2tty = rgb2ansi_;
TTYQUANT()->rgb2ttyf = rgb2ttyf2i_;
TTYQUANT()->tty2rgb = tty2rgb_;
TTYQUANT()->tty2rgbf = tty2rgbf_;
TTYQUANT()->setbg = setbg256_;
TTYQUANT()->setfg = setfg256_;
TTYQUANT()->setbgfg = setbgfg256_;
TTYQUANT()->min = 16;
TTYQUANT()->max = 256;
break;
default:
abort();
}
TTYQUANT()->chans = chans;
TTYQUANT()->alg = alg;
TTYQUANT()->blocks = blocks;
}
INITIALIZER(400, _init_ttyquant,
ttyquantinit(kTtyQuantXterm256, kTtyQuantRgb, kTtyBlocksUnicode));

29
dsp/tty/restore.c Normal file
View file

@ -0,0 +1,29 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/calls/termios.h"
#include "libc/sysv/consts/termios.h"
/**
* Puts teletypewriter back into previous configuration.
*/
int ttyrestore(int ttyoutfd, const struct termios *conf) {
return tcsetattr(ttyoutfd, TCSADRAIN, conf);
}

129
dsp/tty/rgb2ansi.c Normal file
View file

@ -0,0 +1,129 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/core/core.h"
#include "dsp/tty/quant.h"
#include "libc/assert.h"
#include "libc/limits.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/str/str.h"
#define CUTOFF_VALUE 200
#define DIST(X, Y) ((X) - (Y))
#define SQR(X) ((X) * (X))
#define SUM(X, Y, Z) ((X) + (Y) + (Z))
static const uint8_t kXtermCube[] = {0, 0137, 0207, 0257, 0327, 0377};
struct TtyRgb g_ansi2rgb_[256];
static uint8_t g_quant[256];
static uint8_t g_rindex[256];
static uint8_t g_gindex[256];
static uint8_t g_bindex[256];
double g_xterm256_gamma;
struct TtyRgb tty2rgb_(struct TtyRgb rgbxt) {
return g_ansi2rgb_[rgbxt.xt];
}
__m128 tty2rgbf_(struct TtyRgb rgbxt) {
rgbxt = g_ansi2rgb_[rgbxt.xt];
return (__m128){(int)rgbxt.r, (int)rgbxt.g, (int)rgbxt.b} / 255;
}
static int rgb2xterm256_(int r, int g, int b) {
int cerr, gerr, ir, ig, ib, gray, grai, cr, cg, cb, gv;
gray = round(r * .299 + g * .587 + b * .114);
grai = gray > 238 ? 23 : (gray - 3) / 10;
ir = r < 48 ? 0 : r < 115 ? 1 : (r - 35) / 40;
ig = g < 48 ? 0 : g < 115 ? 1 : (g - 35) / 40;
ib = b < 48 ? 0 : b < 115 ? 1 : (b - 35) / 40;
cr = kXtermCube[ir];
cg = kXtermCube[ig];
cb = kXtermCube[ib];
gv = 8 + 10 * grai;
cerr = SQR(DIST(cr, r)) + SQR(DIST(cg, g)) + SQR(DIST(cb, b));
gerr = SQR(DIST(gv, r)) + SQR(DIST(gv, g)) + SQR(DIST(gv, b));
if (cerr <= gerr) {
return 16 + 36 * ir + 6 * ig + ib;
} else {
return 232 + grai;
}
}
/**
* Quantizes RGB to ANSI w/ euclidean distance in 3D color space.
*/
struct TtyRgb rgb2ansi_(int r, int g, int b) {
uint32_t d, least;
size_t c, best, min, max;
r = MAX(MIN(r, 255), 0);
g = MAX(MIN(g, 255), 0);
b = MAX(MIN(b, 255), 0);
min = ttyquant()->min;
max = ttyquant()->max;
if (min == 16 && max == 256) {
return (struct TtyRgb){r, g, b, rgb2xterm256_(r, g, b)};
} else {
least = UINT32_MAX;
best = 0;
for (c = min; c < max; c++) {
d = SUM(SQR(DIST(g_ansi2rgb_[c].r, r)), SQR(DIST(g_ansi2rgb_[c].g, g)),
SQR(DIST(g_ansi2rgb_[c].b, b)));
if (d < least) {
least = d;
best = c;
}
}
return (struct TtyRgb){r, g, b, best};
}
}
static int uncube(int x) {
return x < 48 ? 0 : x < 115 ? 1 : (x - 35) / 40;
}
static optimizesize textstartup void xterm2rgbsetup_(void) {
uint8_t c, y;
uint32_t i, j;
memcpy(g_ansi2rgb_, &kCgaPalette, sizeof(kCgaPalette));
for (i = 0; i < 256; ++i) {
j = uncube(i);
g_quant[i] = kXtermCube[j];
g_rindex[i] = j * 36;
g_gindex[i] = j * 6;
g_bindex[i] = j + 16;
}
for (i = 16; i < 232; ++i) {
g_ansi2rgb_[i].r = kXtermCube[((i - 020) / 044) % 06];
g_ansi2rgb_[i].g = kXtermCube[((i - 020) / 06) % 06];
g_ansi2rgb_[i].b = kXtermCube[(i - 020) % 06];
g_ansi2rgb_[i].xt = i;
}
for (i = 232, c = 8; i < 256; i++, c += 10) {
g_ansi2rgb_[i].r = c;
g_ansi2rgb_[i].g = c;
g_ansi2rgb_[i].b = c;
g_ansi2rgb_[i].xt = i;
}
}
INITIALIZER(301, _init_ansi2rgb, xterm2rgbsetup_());

29
dsp/tty/rgb2ttyf2i.c Normal file
View file

@ -0,0 +1,29 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/quant.h"
#include "libc/bits/xmmintrin.h"
struct TtyRgb rgb2ttyf2i_(__m128 rgb) {
__v4si i4;
rgb *= 255;
/* i4 = __builtin_ia32_cvtps2dq(rgb); */
asm("cvttps2dq\t%0,%1" : "+%x"(rgb), "=x"(i4));
return rgb2tty(i4[0], i4[1], i4[2]);
}

26
dsp/tty/rgb2ttyi2f.c Normal file
View file

@ -0,0 +1,26 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/quant.h"
#include "libc/log/check.h"
#include "libc/macros.h"
struct TtyRgb rgb2ttyi2f_(int r, int g, int b) {
return rgb2ttyf((__m128){r, g, b} / 255);
}

26
dsp/tty/rgb2xterm24.c Normal file
View file

@ -0,0 +1,26 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/quant.h"
#include "libc/macros.h"
struct TtyRgb rgb2xterm24_(int r, int g, int b) {
return (struct TtyRgb){MAX(MIN(r, 255), 0), MAX(MIN(g, 255), 0),
MAX(MIN(b, 255), 0), 0};
}

42
dsp/tty/rgb2xterm24f.c Normal file
View file

@ -0,0 +1,42 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/quant.h"
#include "libc/math.h"
/*
struct TtyRgb rgb2tty24f_(__m128 rgb) {
const __v4si kMax = {255, 255, 255, 255};
const __v4si kMin = {0, 0, 0, 0};
struct TtyRgb res;
__v4si rgb255;
rgb255 = _mm_min_ps(_mm_max_ps(_mm_cvtps_epi32(rgb * 255), kMin), kMax);
res = (struct TtyRgb){rgb255[0], rgb255[1], rgb255[2], rgb255[3]};
return res;
}
*/
struct TtyRgb rgb2tty24f_(__m128 rgb) {
const __m128 kMax = {1, 1, 1, 1};
const __m128 kMin = {0, 0, 0, 0};
struct TtyRgb res;
rgb = _mm_min_ps(_mm_max_ps(rgb, kMin), kMax) * 255;
res = (struct TtyRgb){rgb[0], rgb[1], rgb[2], rgb[3]};
return res;
}

71
dsp/tty/rgb2xterm256.c Normal file
View file

@ -0,0 +1,71 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/rgb2xterm256.h"
/* 1bc */
forceinline int sqr(int x) { return x * x; }
/* forceinline int dst6(int x) { return x * x; } */
int rgb2xterm256v2(int r, int g, int b) {
static const int i2cv[] = {0, 0137, 0207, 0257, 0327, 0377, 0377};
#define v2ci(v) (v < 060 ? 0 : v < 0163 ? 01 : (v - 043) / 050)
#define dst6(A, B, C, a, b, c) (sqr(A - a) + sqr(B - b) + sqr(C - c))
int ir = v2ci(r);
int ig = v2ci(g);
int ib = v2ci(b);
int avg = (r + g + b) / 3;
int cr = i2cv[ir];
int cg = i2cv[ig];
int cb = i2cv[ib];
int gidx = avg > 238 ? 23 : (avg - 3) / 10;
int gv = 8 + 10 * gidx;
int cerr = dst6(cr, cg, cb, r, g, b);
int gerr = dst6(gv, gv, gv, r, g, b);
return cerr <= gerr ? 16 + (36 * ir + 6 * ig + ib) : 232 + gidx;
#undef dst6
#undef cidx
#undef v2ci
}
/* 1e3 */
// Convert RGB24 to xterm-256 8-bit value
// For simplicity, assume RGB space is perceptually uniform.
// There are 5 places where one of two outputs needs to be chosen when
// input is the exact middle:
// - The r/g/b channels and the gray value: choose higher value output
// - If gray and color have same distance from input - choose color
int rgb2xterm256(uint8_t r, uint8_t g, uint8_t b) {
// Calculate the nearest 0-based color index at 16 .. 231
#define v2ci(v) (v < 48 ? 0 : v < 115 ? 1 : (v - 35) / 40)
int ir = v2ci(r), ig = v2ci(g), ib = v2ci(b); // 0..5 each
#define color_index() (36 * ir + 6 * ig + ib) /* 0..215, lazy eval */
// Calculate the nearest 0-based gray index at 232 .. 255
int average = (r + g + b) / 3;
int gray_index = average > 238 ? 23 : (average - 3) / 10; // 0..23
// Calculate the represented colors back from the index
static const int i2cv[6] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
int cr = i2cv[ir], cg = i2cv[ig], cb = i2cv[ib]; // r/g/b, 0..255 each
int gv = 8 + 10 * gray_index; // same value for r/g/b, 0..255
// Return the one which is nearer to the original input rgb value
#define dist_square(A, B, C, a, b, c) \
((A - a) * (A - a) + (B - b) * (B - b) + (C - c) * (C - c))
int color_err = dist_square(cr, cg, cb, r, g, b);
int gray_err = dist_square(gv, gv, gv, r, g, b);
return color_err <= gray_err ? 16 + color_index() : 232 + gray_index;
}

11
dsp/tty/rgb2xterm256.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef COSMOPOLITAN_DSP_TTY_RGB2XTERM256_H_
#define COSMOPOLITAN_DSP_TTY_RGB2XTERM256_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int rgb2xterm256(uint8_t, uint8_t, uint8_t);
int rgb2xterm256v2(int, int, int);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_TTY_RGB2XTERM256_H_ */

30
dsp/tty/savecursor.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
/**
* Asks teletypewriter to push current position.
*/
int ttysavecursor(int ttyfd) { return ttysend(ttyfd, "\e[s"); }
/**
* Asks teletypewriter to pop previous position.
*/
int ttyrestorecursor(int ttyfd) { return ttysend(ttyfd, "\e[u"); }

30
dsp/tty/send.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/str/str.h"
/**
* Sends data to teletypewriter.
*
* This function blocks until the full amount is transmitted.
*
* @return 0 on success, or -1 w/ errno
*/
int ttysend(int fd, const char *str) { return ttywrite(fd, str, strlen(str)); }

41
dsp/tty/sendtitle.c Normal file
View file

@ -0,0 +1,41 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/alg/arraylist2.h"
#include "libc/runtime/gc.h"
#include "libc/x/x.h"
/**
* Changes text in title bar of pseudo-teletypewriter window.
*
* @param title is trustworthy text without any BEL characters
* @param ti comes from ttyident() and null means no-op
*/
int ttysendtitle(int ttyfd, const char *title, const struct TtyIdent *ti) {
if (ti) {
if (ti->id == kTtyIdScreen) {
return ttysend(ttyfd, gc(xstrcat("\eP\e]0;", title, "\a\e\\")));
} else {
return ttysend(ttyfd, gc(xstrcat("\e]0;", title, "\a")));
}
} else {
return 0;
}
}

25
dsp/tty/setansipalette.c Normal file
View file

@ -0,0 +1,25 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/quant.h"
#include "libc/str/str.h"
void setansipalette(ttypalette_t palette) {
memcpy(g_ansi2rgb_, palette, sizeof(struct TtyRgb) * 2 * 8);
}

43
dsp/tty/setbgfg16.c Normal file
View file

@ -0,0 +1,43 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/itoa8.h"
#include "dsp/tty/quant.h"
#include "libc/limits.h"
static char *ansitoa(char *p, unsigned xt, unsigned base) {
if (xt >= 8) xt -= 8, base += 60;
return itoa8(p, xt + base);
}
static char *setansibgfg(char *p, unsigned bg, unsigned fg) {
*p++ = '\e';
*p++ = '[';
if (bg != -1u) p = ansitoa(p, bg, 40);
if (bg != -1u && fg != -1u) *p++ = ';';
if (fg != -1u) p = ansitoa(p, fg, 30);
*p++ = 'm';
return p;
}
char *setbg16_(char *p, struct TtyRgb bg) { return setansibgfg(p, bg.xt, -1u); }
char *setfg16_(char *p, struct TtyRgb fg) { return setansibgfg(p, -1u, fg.xt); }
char *setbgfg16_(char *p, struct TtyRgb bg, struct TtyRgb fg) {
return setansibgfg(p, bg.xt, fg.xt);
}

54
dsp/tty/setbgfg24.c Normal file
View file

@ -0,0 +1,54 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/itoa8.h"
#include "dsp/tty/quant.h"
#include "libc/str/str.h"
static char *rgbcpy(char *p, struct TtyRgb bg) {
memcpy(p, ";2;\0", 4);
p = itoa8(p + 3, bg.r);
*p++ = ';';
p = itoa8(p, bg.g);
*p++ = ';';
return itoa8(p, bg.b);
}
char *setbg24_(char *p, struct TtyRgb bg) {
memcpy(p, "\e[48", 4);
p = rgbcpy(p + 4, bg);
*p++ = 'm';
return p;
}
char *setfg24_(char *p, struct TtyRgb fg) {
memcpy(p, "\e[38", 4);
p = rgbcpy(p + 4, fg);
*p++ = 'm';
return p;
}
char *setbgfg24_(char *p, struct TtyRgb bg, struct TtyRgb fg) {
memcpy(p, "\e[48", 4);
p = rgbcpy(p + 4, bg);
memcpy(p, ";38\0", 4);
p = rgbcpy(p + 3, fg);
*p++ = 'm';
return p;
}

48
dsp/tty/setbgfg256.c Normal file
View file

@ -0,0 +1,48 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/itoa8.h"
#include "dsp/tty/quant.h"
char *setbg256_(char *p, struct TtyRgb bg) {
memcpy(p, "\e[48", 4);
memcpy(p + 4, ";5;\0", 4);
p = itoa8(p + 7, bg.xt);
*p++ = 'm';
return p;
}
char *setfg256_(char *p, struct TtyRgb fg) {
memcpy(p, "\e[38", 4);
memcpy(p + 4, ";5;\0", 4);
p = itoa8(p + 7, fg.xt);
*p++ = 'm';
return p;
}
char *setbgfg256_(char *p, struct TtyRgb bg, struct TtyRgb fg) {
memcpy(p, "\e[48", 4);
memcpy(p + 4, ";5;\0", 4);
p = itoa8(p + 7, bg.xt);
memcpy(p, ";38;", 4);
memcpy(p + 4, "5;\0", 4);
p = itoa8(p + 6, fg.xt);
*p++ = 'm';
return p;
}

52
dsp/tty/setraw.c Normal file
View file

@ -0,0 +1,52 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/calls/termios.h"
#include "libc/sysv/consts/termios.h"
/**
* Enables direct teletypewriter communication.
*
* @see ttyconfig(), ttyrestore()
*/
int ttysetraw(struct termios *conf, int64_t flags) {
conf->c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON);
conf->c_lflag &= ~(IEXTEN | ICANON);
conf->c_cflag &= ~(CSIZE | PARENB);
conf->c_cflag |= CS8;
conf->c_iflag |= IUTF8;
/* if (flags & kTtyLfToCrLf) { */
/* /\* conf->c_oflag &= ~(OLCUC | OCRNL | ONLRET | OFILL | OFDEL); *\/ */
/* /\* conf->c_oflag |= ONLCR | ONOCR; *\/ */
/* conf->c_oflag |= ONLCR; */
/* } else { */
/* conf->c_oflag &= ~OPOST; */
/* } */
if (!(flags & kTtySigs)) {
conf->c_iflag &= ~(IGNBRK | BRKINT);
conf->c_lflag &= ~(ISIG);
}
if (flags & kTtyEcho) {
conf->c_lflag |= ECHO | ECHONL;
} else {
conf->c_lflag &= ~(ECHO | ECHONL);
}
return 0;
}

34
dsp/tty/setrawdeadline.c Normal file
View file

@ -0,0 +1,34 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/assert.h"
#include "libc/calls/termios.h"
#include "libc/sysv/consts/termios.h"
/**
* Enables direct teletypewriter communication w/ read timeouts.
* @see ttyconfig(), ttyrestore()
*/
int ttysetrawdeadline(struct termios *conf, int64_t deciseconds) {
assert(0 < deciseconds && deciseconds < 256);
conf->c_cc[VMIN] = 0;
conf->c_cc[VTIME] = deciseconds;
return ttysetraw(conf, 0);
}

33
dsp/tty/setrawmode.c Normal file
View file

@ -0,0 +1,33 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/tty.h"
#include "libc/assert.h"
#include "libc/calls/termios.h"
#include "libc/sysv/consts/termios.h"
/**
* Enables blocking raw mode for teletypewriter w/ recommended settings.
* @see ttyconfig(), ttyrestore()
*/
int ttysetrawmode(struct termios *conf, int64_t flags) {
conf->c_cc[VMIN] = 1;
conf->c_cc[VTIME] = 1;
return ttysetraw(conf, flags);
}

59
dsp/tty/tty.h Normal file
View file

@ -0,0 +1,59 @@
#ifndef COSMOPOLITAN_DSP_TTY_TTY_H_
#define COSMOPOLITAN_DSP_TTY_TTY_H_
#define kTtyIdScreen 83
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct FILE;
struct termios;
struct TtyIdent {
int id; /* first number sent back by \e[>c */
int version; /* second number sent back by \e[>c */
struct TtyIdent *next; /* yo dawg */
};
struct TtyCursor {
int y;
int x;
int bg;
int fg;
};
enum TtyRawFlags {
kTtyEcho = 1 << 0, /* echo input */
kTtyCursor = 1 << 1, /* show cursor */
kTtySigs = 1 << 2, /* auto raise() on CTRL+C, CTRL+Z, and CTRL+\ */
kTtyLfToCrLf = 1 << 3, /* enables unix newline magic */
};
typedef int (*ttyconf_f)(struct termios *, int64_t);
int ttyraw(enum TtyRawFlags);
int ttyhidecursor(int);
int ttyshowcursor(int);
int ttysavecursor(int);
int ttyrestorecursor(int);
int ttyenablealtbuf(int);
int ttydisablealtbuf(int);
int ttysend(int, const char *);
int ttywrite(int, const void *, size_t);
int ttysendtitle(int, const char *, const struct TtyIdent *);
int ttyident(struct TtyIdent *, int, int);
void ttyidentclear(struct TtyIdent *);
char *ttydescribe(char *, size_t, const struct TtyIdent *);
int ttyconfig(int, ttyconf_f, int64_t, const struct termios *);
int ttyrestore(int, const struct termios *);
int ttysetrawdeadline(struct termios *, int64_t);
int ttysetrawmode(struct termios *, int64_t);
int ttysetraw(struct termios *, int64_t);
char *ttymove(struct TtyCursor *, char *, int, int)
paramsnonnull() returnsnonnull;
void ttyhisto(uint32_t[hasatleast 256], uint8_t[hasatleast 256],
const uint8_t *, const uint8_t *, size_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_DSP_TTY_TTY_H_ */

70
dsp/tty/tty.mk Normal file
View file

@ -0,0 +1,70 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += DSP_TTY
DSP_TTY_ARTIFACTS += DSP_TTY_A
DSP_TTY = $(DSP_TTY_A_DEPS) $(DSP_TTY_A)
DSP_TTY_A = o/$(MODE)/dsp/tty/tty.a
DSP_TTY_A_FILES := $(wildcard dsp/tty/*)
DSP_TTY_A_HDRS = $(filter %.h,$(DSP_TTY_A_FILES))
DSP_TTY_A_SRCS_S = $(filter %.S,$(DSP_TTY_A_FILES))
DSP_TTY_A_SRCS_C = $(filter %.c,$(DSP_TTY_A_FILES))
DSP_TTY_A_SRCS = \
$(DSP_TTY_A_SRCS_S) \
$(DSP_TTY_A_SRCS_C)
DSP_TTY_A_OBJS = \
$(DSP_TTY_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(DSP_TTY_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(DSP_TTY_A_SRCS_C:%.c=o/$(MODE)/%.o)
DSP_TTY_A_CHECKS = \
$(DSP_TTY_A).pkg \
$(DSP_TTY_A_HDRS:%=o/$(MODE)/%.ok)
DSP_TTY_A_DIRECTDEPS = \
DSP_CORE \
LIBC_ALG \
LIBC_CALLS \
LIBC_FMT \
LIBC_LOG \
LIBC_RUNTIME \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_KERNELBASE \
LIBC_STR \
LIBC_STDIO \
LIBC_STUBS \
LIBC_SOCK \
LIBC_SYSV \
LIBC_TINYMATH \
LIBC_TIME \
LIBC_X \
LIBC_UNICODE
DSP_TTY_A_DEPS := \
$(call uniq,$(foreach x,$(DSP_TTY_A_DIRECTDEPS),$($(x))))
$(DSP_TTY_A): dsp/tty/ \
$(DSP_TTY_A).pkg \
$(DSP_TTY_A_OBJS)
$(DSP_TTY_A).pkg: \
$(DSP_TTY_A_OBJS) \
$(foreach x,$(DSP_TTY_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/dsp/tty/ttyraster.o: \
OVERRIDE_CFLAGS += \
$(MATHEMATICAL)
DSP_TTY_LIBS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x)))
DSP_TTY_SRCS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x)_SRCS))
DSP_TTY_HDRS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x)_HDRS))
DSP_TTY_CHECKS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x)_CHECKS))
DSP_TTY_OBJS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x)_OBJS))
$(DSP_TTY_OBJS): $(BUILD_FILES) dsp/tty/tty.mk
.PHONY: o/$(MODE)/dsp/tty
o/$(MODE)/dsp/tty: $(DSP_TTY_CHECKS)

24
dsp/tty/tty2rgb24.c Normal file
View file

@ -0,0 +1,24 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/tty/quant.h"
struct TtyRgb tty2rgb24_(struct TtyRgb rgbxt) {
return rgbxt;
}

Some files were not shown because too many files have changed in this diff Show more