Move to Git

This commit is contained in:
mid
2024-06-26 20:47:26 +03:00
commit 18da1dabcd
34 changed files with 5999 additions and 0 deletions

26
hi/bs.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef _CUTI_BS_H
#define _CUTI_BS_H
#ifdef __cplusplus
extern "C" {
#endif
#define CUTIHI_BS_FLAG_KEY 1
typedef struct {
uint64_t timestamp;
uint32_t sz;
uint8_t flags;
void *ptr;
} CHiBSFrame;
typedef struct {
size_t count;
CHiBSFrame data[];
} CHiBSFrames;
#ifdef __cplusplus
}
#endif
#endif

3
hi/defs.h Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
#define CUTIVIS __attribute__((visibility("default")))

39
hi/img.c Normal file
View File

@@ -0,0 +1,39 @@
#include"img.h"
#include<mm_malloc.h>
#include<string.h>
CUTIVIS CHiImage* CHi_Image_New(uint8_t bpc, uint8_t channels, uint16_t stride, uint16_t width, uint16_t height, void *data) {
CHiImage *img = malloc(sizeof(*img));
img->bpc = bpc;
img->channels = channels;
img->stride = stride;
img->width = width;
img->height = height;
if(data) img->data16 = data;
else img->data16 = _mm_malloc(bpc * stride * height, 16);
img->owned = !data;
return img;
}
CUTIVIS void CHi_Image_Free(CHiImage *img) {
if(img->owned) {
_mm_free(img->data16);
}
free(img);
}
CUTIVIS void CHi_Restride(const void *oldbuf_, void *newbuf_, uint16_t oldStride, uint16_t newStride, uint16_t rows) {
const uint8_t *oldbuf = oldbuf_;
uint8_t *newbuf = newbuf_;
if(oldStride == newStride && oldbuf == newbuf) {
return;
}
while(rows) {
uint16_t row = --rows;
memmove(&newbuf[newStride * row], &oldbuf[oldStride * row], oldStride);
}
}

29
hi/img.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include<stdint.h>
#include"defs.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct CHiImage {
uint8_t bpc;
uint8_t channels;
uint16_t stride;
uint16_t width;
uint16_t height;
union {
uint16_t *data16;
};
uint8_t owned;
} CHiImage;
CUTIVIS CHiImage* CHi_Image_New(uint8_t bpc, uint8_t channels, uint16_t stride, uint16_t width, uint16_t height, void *data);
CUTIVIS void CHi_Image_Free(CHiImage *img);
CUTIVIS void CHi_Restride(const void *oldbuf, void *newbuf, uint16_t oldStride, uint16_t newStride, uint16_t rows);
#ifdef __cplusplus
}
#endif

1523
hi/kumb.h Normal file

File diff suppressed because it is too large Load Diff

114
hi/linearity.h Normal file
View File

@@ -0,0 +1,114 @@
#pragma once
#define SSE_MATHFUN_WITH_CODE
#include"kumb.h"
// exp2f4 and log2f4 by Jose Fonseca (MIT License)
#define EXP_POLY_DEGREE 3
#define POLY0(x, c0) _mm_set1_ps(c0)
#define POLY1(x, c0, c1) _mm_add_ps(_mm_mul_ps(POLY0(x, c1), x), _mm_set1_ps(c0))
#define POLY2(x, c0, c1, c2) _mm_add_ps(_mm_mul_ps(POLY1(x, c1, c2), x), _mm_set1_ps(c0))
#define POLY3(x, c0, c1, c2, c3) _mm_add_ps(_mm_mul_ps(POLY2(x, c1, c2, c3), x), _mm_set1_ps(c0))
#define POLY4(x, c0, c1, c2, c3, c4) _mm_add_ps(_mm_mul_ps(POLY3(x, c1, c2, c3, c4), x), _mm_set1_ps(c0))
#define POLY5(x, c0, c1, c2, c3, c4, c5) _mm_add_ps(_mm_mul_ps(POLY4(x, c1, c2, c3, c4, c5), x), _mm_set1_ps(c0))
static inline __m128 exp2f4(__m128 x)
{
__m128i ipart;
__m128 fpart, expipart, expfpart;
x = _mm_min_ps(x, _mm_set1_ps( 129.00000f));
x = _mm_max_ps(x, _mm_set1_ps(-126.99999f));
/* ipart = int(x - 0.5) */
ipart = _mm_cvtps_epi32(_mm_sub_ps(x, _mm_set1_ps(0.5f)));
/* fpart = x - ipart */
fpart = _mm_sub_ps(x, _mm_cvtepi32_ps(ipart));
/* expipart = (float) (1 << ipart) */
expipart = _mm_castsi128_ps(_mm_slli_epi32(_mm_add_epi32(ipart, _mm_set1_epi32(127)), 23));
/* minimax polynomial fit of 2**x, in range [-0.5, 0.5[ */
#if EXP_POLY_DEGREE == 5
expfpart = POLY5(fpart, 9.9999994e-1f, 6.9315308e-1f, 2.4015361e-1f, 5.5826318e-2f, 8.9893397e-3f, 1.8775767e-3f);
#elif EXP_POLY_DEGREE == 4
expfpart = POLY4(fpart, 1.0000026f, 6.9300383e-1f, 2.4144275e-1f, 5.2011464e-2f, 1.3534167e-2f);
#elif EXP_POLY_DEGREE == 3
expfpart = POLY3(fpart, 9.9992520e-1f, 6.9583356e-1f, 2.2606716e-1f, 7.8024521e-2f);
#elif EXP_POLY_DEGREE == 2
expfpart = POLY2(fpart, 1.0017247f, 6.5763628e-1f, 3.3718944e-1f);
#else
#error
#endif
return _mm_mul_ps(expipart, expfpart);
}
#define LOG_POLY_DEGREE 5
static inline __m128 log2f4(__m128 x)
{
__m128i exp = _mm_set1_epi32(0x7F800000);
__m128i mant = _mm_set1_epi32(0x007FFFFF);
__m128 one = _mm_set1_ps( 1.0f);
__m128i i = _mm_castps_si128(x);
__m128 e = _mm_cvtepi32_ps(_mm_sub_epi32(_mm_srli_epi32(_mm_and_si128(i, exp), 23), _mm_set1_epi32(127)));
__m128 m = _mm_or_ps(_mm_castsi128_ps(_mm_and_si128(i, mant)), one);
__m128 p;
/* Minimax polynomial fit of log2(x)/(x - 1), for x in range [1, 2[ */
#if LOG_POLY_DEGREE == 6
p = POLY5( m, 3.1157899f, -3.3241990f, 2.5988452f, -1.2315303f, 3.1821337e-1f, -3.4436006e-2f);
#elif LOG_POLY_DEGREE == 5
p = POLY4(m, 2.8882704548164776201f, -2.52074962577807006663f, 1.48116647521213171641f, -0.465725644288844778798f, 0.0596515482674574969533f);
#elif LOG_POLY_DEGREE == 4
p = POLY3(m, 2.61761038894603480148f, -1.75647175389045657003f, 0.688243882994381274313f, -0.107254423828329604454f);
#elif LOG_POLY_DEGREE == 3
p = POLY2(m, 2.28330284476918490682f, -1.04913055217340124191f, 0.204446009836232697516f);
#else
#error
#endif
/* This effectively increases the polynomial degree by one, but ensures that log2(1) == 0*/
p = _mm_mul_ps(p, _mm_sub_ps(m, one));
return _mm_add_ps(p, e);
}
__attribute__((optimize("O3"))) static inline __m128i apply_gamma_epi32(__m128i z, __m128 gamma) {
__m128 zf = _mm_cvtepi32_ps(z);
zf = _mm_mul_ps(zf, _mm_set1_ps(1.f / 65535));
zf = log2f4(zf);
zf = _mm_mul_ps(zf, gamma);
zf = exp2f4(zf);
zf = _mm_mul_ps(zf, _mm_set1_ps(65535));
z = _mm_cvtps_epi32(zf);
/* Sometimes overflow causes the top 16 bits to be non-zero (e.g. when log(0) makes NaN) */
/* Those must be masked out */
z = _mm_and_si128(z, _mm_set1_epi32(0x0000FFFF));
return z;
}
__attribute__((optimize("O3"))) static inline __m128i apply_gamma_epi16(__m128i z, __m128 gamma) {
__m128i low = apply_gamma_epi32(_mm_unpacklo_epi16(z, _mm_setzero_si128()), gamma);
__m128i high = apply_gamma_epi32(_mm_unpackhi_epi16(z, _mm_setzero_si128()), gamma);
low = _mm_hadd_epi16(low, low);
low = _mm_and_si128(low, _mm_set_epi32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF));
high = _mm_hadd_epi16(high, high);
high = _mm_slli_si128(high, 8);
return _mm_or_si128(low, high);
}

19
hi/loopback.c Normal file
View File

@@ -0,0 +1,19 @@
#include<alsa/asoundlib.h>
CUTIVIS CHiPubNode *CHi_Loopback() {
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
printf("GOO %i\n", snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0));
printf("GOO %i\n", snd_pcm_hw_params_malloc(&params));
printf("GOO %i\n", snd_pcm_hw_params_any(handle, params));
printf("GOO %i\n", snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED));
printf("GOO %i\n", snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE));
printf("GOO %i\n", snd_pcm_hw_params_set_rate_near(handle, params, &(int) {48000}));
printf("GOO %i\n", snd_pcm_hw_params_set_channels(handle, params, 1));
printf("GOO %i\n", snd_pcm_hw_params(handle, aprams));
snd_pcm_hw_params_free(params);
printf("GOO %i\n", snd_pcm_prepare(handle));
}

209
hi/microphone.c Normal file
View File

@@ -0,0 +1,209 @@
#include"node.h"
#include<portaudio.h>
#include"img.h"
#include<stdlib.h>
#include<stdio.h>
#include<math.h>
#include<stdatomic.h>
#include<string.h>
#include"microphone.h"
#define pabufsize (48000*5)
typedef struct CHiMicrophoneNode {
CHiPubNode pub;
PaStream *paStream;
int16_t paBuffer[pabufsize];
atomic_size_t paBufferWriteIdx;
atomic_size_t paBufferReadIdx;
} CHiMicrophoneNode;
static int pacallback(const void *input_, void *output, unsigned long samples, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags flags, void *ud) {
CHiMicrophoneNode *node = ud;
const int16_t *input = input_;
size_t writeidx = node->paBufferWriteIdx;
while(writeidx + samples >= pabufsize) {
memcpy(node->paBuffer + writeidx, input, (pabufsize - writeidx) * sizeof(*node->paBuffer));
samples -= pabufsize - writeidx;
input += pabufsize - writeidx;
writeidx = 0;
}
memcpy(node->paBuffer + writeidx, input, samples * sizeof(*node->paBuffer));
writeidx = (writeidx + samples) % pabufsize;
node->paBufferWriteIdx = writeidx;
/*static size_t total = 0;
for(size_t i = 0; i < pabufsize; i++) {
paBuffer[paBufferWriteIdx] = sin(total++ * 440.0 / 24000 * 3.141592653) * 0.1;
paBufferWriteIdx = (paBufferWriteIdx + 1) % pabufsize;
}*/
return paContinue;
}
static int microphone_start(CHiPubNode *pubn) {
CHiMicrophoneNode *node = (void*) pubn;
PaStreamParameters params = {
.device = pubn->sinks[0].data.vec4[0],
.channelCount = 1,
.sampleFormat = paInt16,
.suggestedLatency = Pa_GetDeviceInfo(pubn->sinks[0].data.vec4[0])->defaultLowInputLatency,
};
Pa_OpenStream(&node->paStream, &params, NULL, 48000, 0, paNoFlag, pacallback, pubn);
Pa_StartStream(node->paStream);
return 1;
}
static int microphone_stop(CHiPubNode *pubn) {
CHiMicrophoneNode *node = (void*) pubn;
Pa_StopStream(node->paStream);
Pa_CloseStream(node->paStream);
return 1;
}
static int microphone_perform(CHiPubNode *pubn) {
CHiMicrophoneNode *node = (void*) pubn;
if(pubn->sources[0].data.sample) {
CHi_Image_Free(pubn->sources[0].data.sample);
}
size_t width = roundf(CHi_Time_GetDelta(pubn->ng) * 48000.f);
do {
}while((node->paBufferWriteIdx - node->paBufferReadIdx + pabufsize) % pabufsize < width); // Wait until available
CHiImage *ret = CHi_Image_New(2, 1, 2 * width, width, 1, NULL);
if(node->paBufferReadIdx + width >= pabufsize) {
memcpy(ret->data16, node->paBuffer + node->paBufferReadIdx, sizeof(*node->paBuffer) * (pabufsize - node->paBufferReadIdx));
memcpy(ret->data16 + pabufsize - node->paBufferReadIdx, node->paBuffer, sizeof(*node->paBuffer) * (width - pabufsize + node->paBufferReadIdx));
node->paBufferReadIdx = node->paBufferReadIdx + width - pabufsize;
} else {
memcpy(ret->data16, node->paBuffer + node->paBufferReadIdx, sizeof(*node->paBuffer) * width);
node->paBufferReadIdx = (node->paBufferReadIdx + width) % pabufsize;
}
pubn->sources[0].type = CUTIHI_VAL_SAMPLE;
pubn->sources[0].data.sample = ret;
pubn->clean = 0;
return 1;
}
CUTIVIS CHiPubNode *CHi_Microphone() {
static int inited = 0;
if(!inited) {
Pa_Initialize();
inited = 1;
}
CHiPubNode *n = calloc(1, sizeof(CHiMicrophoneNode));
n->type = CUTIHI_T('CInA','udio');
n->Start = microphone_start;
n->Perform = microphone_perform;
n->Stop = microphone_stop;
n->clean = 0;
n->sinkCount = 1;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount);
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), n->sourceCount);
return n;
}
struct CHiExportWavNode {
CHiPubNode pubn;
FILE *output;
};
CUTIVIS int CHi_ExportWav_Start(CHiPubNode *pubn) {
struct CHiExportWavNode *n = (struct CHiExportWavNode*) pubn;
n->output = fopen(CHi_Crawl(&pubn->sinks[0])->data.text, "wb");
struct __attribute__((packed)) {
uint32_t ckID;
uint32_t ckSize;
uint32_t waveID;
} header = {.ckID = 'FFIR', .ckSize = 0, .waveID = 'EVAW'};
fwrite(&header, sizeof(header), 1, n->output);
struct __attribute__((packed)) {
uint32_t ckID;
uint32_t ckSize;
uint16_t wFormatTag;
uint16_t nChannels;
uint32_t nSamplesPerSec;
uint32_t nAvgBytesPerSec;
uint16_t nBlockAlign;
uint16_t wBitsPerSample;
} chunk0 = {.ckID = ' tmf', .ckSize = 16, .wFormatTag = 1 /* float */, .nChannels = 1, .nSamplesPerSec = 48000, .nAvgBytesPerSec = 48000 * 2, .nBlockAlign = 4, .wBitsPerSample = 16};
fwrite(&chunk0, sizeof(chunk0), 1, n->output);
struct __attribute__((packed)) {
uint32_t ckID;
uint32_t ckSize;
} chunk1 = {.ckID = 'atad', .ckSize = 0};
fwrite(&chunk1, sizeof(chunk1), 1, n->output);
return 1;
}
static int exportwav_perform(CHiPubNode *pubn) {
struct CHiExportWavNode *n = (struct CHiExportWavNode*) pubn;
CHiImage *buf = CHi_Crawl(&pubn->sinks[1])->data.sample;
fwrite(buf->data16, 2, buf->width, n->output);
pubn->clean = 0;
return 1;
}
CUTIVIS int CHi_ExportWav_Stop(CHiPubNode *pubn) {
struct CHiExportWavNode *n = (struct CHiExportWavNode*) pubn;
/* Fill size info in headers. */
uint32_t sz = ftell(n->output) - 8;
fseek(n->output, 4, SEEK_SET);
fwrite(&sz, sizeof(sz), 1, n->output);
sz -= 36;
fseek(n->output, 32, SEEK_CUR);
fwrite(&sz, sizeof(sz), 1, n->output);
fclose(n->output);
return 1;
}
CUTIVIS CHiPubNode *CHi_ExportWav() {
struct CHiExportWavNode *n = malloc(sizeof(*n));
n->pubn.type = CUTIHI_T('CExp','Wave');
n->pubn.Start = CHi_ExportWav_Start;
n->pubn.Perform = exportwav_perform;
n->pubn.Stop = CHi_ExportWav_Stop;
n->pubn.clean = 0;
n->pubn.sinkCount = 2;
n->pubn.sinks = calloc(sizeof(*n->pubn.sinks), n->pubn.sinkCount);
n->pubn.sourceCount = 0;
n->pubn.sources = NULL;
return &n->pubn;
}
size_t CHi_Microphone_GetSourceCount() {
return Pa_GetDeviceCount();
}
const char *CHi_Microphone_GetSourceName(size_t i) {
return Pa_GetDeviceInfo(i)->name;
}
size_t CHi_Microphone_GetNextSource(size_t i) {
i++;
while(i < CHi_Microphone_GetSourceCount()) {
if(Pa_GetDeviceInfo(i)->maxInputChannels > 0) break;
i++;
}
return i;
}

18
hi/microphone.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef _CUTIHI_MICROPHONE_H
#define _CUTIHI_MICROPHONE_H
#include<stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
CUTIVIS size_t CHi_Microphone_GetSourceCount();
CUTIVIS const char *CHi_Microphone_GetSourceName(size_t);
CUTIVIS size_t CHi_Microphone_GetNextSource(size_t);
#ifdef __cplusplus
}
#endif
#endif

11
hi/mode.c Normal file
View File

@@ -0,0 +1,11 @@
#include"mode.h"
static CHiMode moed;
CUTIVIS void CHi_SetMode(CHiMode mode) {
moed = mode;
}
CUTIVIS CHiMode CHi_GetMode() {
return moed;
}

19
hi/mode.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include"defs.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
CUTIHI_MODE_LIVE,
CUTIHI_MODE_OFFLINE
} CHiMode;
CUTIVIS void CHi_SetMode(CHiMode mode);
CUTIVIS CHiMode CHi_GetMode();
#ifdef __cplusplus
}
#endif

899
hi/node.c Normal file
View File

@@ -0,0 +1,899 @@
#include"node.h"
#include<stdlib.h>
#include"img.h"
#include<sail/sail.h>
#include<sail-manip/sail-manip.h>
#include<assert.h>
#include<string.h>
#include<tmmintrin.h>
#include<smmintrin.h>
#include<pango/pango.h>
#include<pango/pangoft2.h>
#include<freetype/ftbitmap.h>
#include"mode.h"
#include<math.h>
#include<sched.h>
#include<limits.h>
#include"linearity.h"
static size_t bisect(const void *key, const void *base, size_t nmemb, size_t size, ssize_t(*compar)(const void*, const void*)) {
size_t low = 0, high = nmemb;
while(low < high) {
size_t middle = (low + high) / 2;
if(compar((const void*) ((uintptr_t) base + size * middle), key) < 0) {
low = middle + 1;
} else {
high = middle;
}
}
return low;
}
static ssize_t float_compar(const void *A, const void *B) {
float a = *(float*) A;
float b = *(float*) B;
return (a > b) - (a < b);
}
static int adjacencycmp(const void *a, const void *b) {
size_t v = (uintptr_t) ((CHiAdjacency*) a)[0] - (uintptr_t) ((CHiAdjacency*) b)[0];
return v ? v : (uintptr_t) ((CHiAdjacency*) a)[1] - (uintptr_t) ((CHiAdjacency*) b)[1];
}
static void adjacency_add(CHiPubNode *source, CHiPubNode *sink) {
CHiNodeGraph *ng = source->ng;
if(ng->adjacencyCount == ng->adjacencyCapacity) {
ng->adjacencies = realloc(ng->adjacencies, sizeof(CHiAdjacency) * (ng->adjacencyCapacity *= 2));
}
ng->adjacencies[ng->adjacencyCount][0] = source;
ng->adjacencies[ng->adjacencyCount][1] = sink;
ng->adjacencyCount++;
qsort(ng->adjacencies, ng->adjacencyCount, sizeof(CHiAdjacency), adjacencycmp);
}
static void adjacency_remove(CHiPubNode *source, CHiPubNode *sink) {
CHiNodeGraph *ng = source->ng;
CHiAdjacency *adj = bsearch(&(CHiAdjacency) {source, sink}, ng->adjacencies, ng->adjacencyCount, sizeof(CHiAdjacency), adjacencycmp);
if(adj) {
memmove(adj, adj + 1, sizeof(CHiAdjacency) * (ng->adjacencyCount - (adj - ng->adjacencies) - 1));
ng->adjacencyCount--;
}
}
CUTIVIS CHiNodeGraph *CHi_NewNodeGraph() {
CHiNodeGraph *ret = calloc(1, sizeof(*ret));
ret->count = 0;
ret->nodes = malloc(sizeof(*ret->nodes) * (ret->capacity = 8));
ret->eventOnStopComplete = NULL;
ret->eventOnFrameComplete = NULL;
ret->compilationStatus = CUTIHI_COMP_READY;
ret->adjacencyCount = 0;
ret->adjacencyCapacity = 8;
ret->adjacencies = malloc(sizeof(CHiAdjacency) * ret->adjacencyCapacity);
return ret;
}
CUTIVIS CHiValue *CHi_Crawl(CHiValue *v) {
while(v->type == CUTIHI_VAL_LINKED || v->type == CUTIHI_VAL_KEYED) {
if(v->type == CUTIHI_VAL_LINKED) {
v = &v->data.linked.to->sources[v->data.linked.idx];
} else if(v->type == CUTIHI_VAL_KEYED) {
v = &v->data.keyed->current;
}
}
return v;
}
CUTIVIS void CHi_RegisterNode(CHiNodeGraph* ng, CHiPubNode* n) {
if(ng->count == ng->capacity) {
ng->nodes = realloc(ng->nodes, sizeof(*ng->nodes) * (ng->capacity = ng->capacity * 3 / 2));
}
ng->nodes[ng->count++] = n;
n->ng = ng;
}
CUTIVIS void CHi_MakeDirty(CHiNodeGraph *ng, CHiPubNode *n) {
for(size_t i = 0; i < ng->count; i++) {
}
}
static int dfs_visit(size_t *resultCount, CHiPubNode ***result, CHiPubNode *n) {
if(n->_dfsmark == 2) return 1;
else if(n->_dfsmark == 1) return 0;
n->_dfsmark = 1;
for(size_t s = 0; s < n->sinkCount; s++) {
if(n->sinks[s].type == CUTIHI_VAL_LINKED) {
if(!dfs_visit(resultCount, result, n->sinks[s].data.linked.to)) {
return 0;
}
}
}
n->_dfsmark++;
(*result)[(*resultCount)++] = n;
return 1;
}
static int topological_sort(CHiNodeGraph *ng) {
size_t resultCount = 0;
CHiPubNode **result = malloc(sizeof(*result) * ng->capacity);
for(size_t i = 0; i < ng->count; i++) {
ng->nodes[i]->_dfsmark = 0;
}
for(size_t i = 0; i < ng->count; i++) {
if(!dfs_visit(&resultCount, &result, ng->nodes[i])) {
free(result);
return 0;
}
}
assert(resultCount == ng->count);
free(ng->nodes);
ng->nodes = result;
return 1;
}
CUTIVIS int CHi_ConfigureSink(CHiPubNode *n, size_t i, CHiValue v) {
if(n->sinks[i].type == CUTIHI_VAL_KEYED) {
n->sinks[i].data.keyed->current = v;
return 1;
}
if(v.type == CUTIHI_VAL_LINKED && n == v.data.linked.to) return 0;
CHiValue old = n->sinks[i];
if(old.type == CUTIHI_VAL_LINKED) {
adjacency_remove(old.data.linked.to, n);
}
n->sinks[i] = v;
if(n->ng && !topological_sort(n->ng)) {
n->sinks[i] = old;
if(old.type == CUTIHI_VAL_LINKED) {
adjacency_add(old.data.linked.to, n);
}
return 0;
}
if(v.type == CUTIHI_VAL_LINKED) {
adjacency_add(v.data.linked.to, n);
}
return 1;
}
CUTIVIS void CHi_MakeKeyframe(CHiNodeGraph *ng, CHiPubNode *n, size_t i) {
if(n->sinks[i].type != CUTIHI_VAL_KEYED) {
CHiKeyframes *kfs = calloc(1, sizeof(*kfs));
kfs->type = n->sinks[i].type;
kfs->count = 1;
kfs->times = malloc(sizeof(*kfs->times));
*kfs->times = ng->time;
kfs->values = malloc(sizeof(*kfs->values));
memcpy(kfs->values, &n->sinks[i].data, sizeof(CHiValueRaw));
memcpy(&kfs->current, &n->sinks[i], sizeof(CHiValueRaw));
kfs->node = n;
n->sinks[i].type = CUTIHI_VAL_KEYED;
n->sinks[i].data.keyed = kfs;
ng->keyframesList.keyframes = realloc(ng->keyframesList.keyframes, sizeof(*ng->keyframesList.keyframes) * (++ng->keyframesList.count));
ng->keyframesList.keyframes[ng->keyframesList.count - 1] = kfs;
} else {
CHiKeyframes *kfs = n->sinks[i].data.keyed;
float now = ng->time;
size_t idx = bisect(&now, kfs->times, kfs->count, sizeof(now), float_compar);
if(idx < kfs->count && kfs->times[idx] == now) {
kfs->values[idx] = kfs->current.data;
} else {
kfs->count++;
kfs->values = realloc(kfs->values, sizeof(*kfs->values) * kfs->count);
kfs->times = realloc(kfs->times, sizeof(*kfs->times) * kfs->count);
memmove(kfs->values + idx + 1, kfs->values + idx, sizeof(*kfs->values) * (kfs->count - idx - 1));
memmove(kfs->times + idx + 1, kfs->times + idx, sizeof(*kfs->times) * (kfs->count - idx - 1));
kfs->values[idx] = kfs->current.data;
kfs->times[idx] = now;
}
}
}
CUTIVIS size_t CHi_MoveKeyframe(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx, float to) {
CHiValueRaw val = kfs->values[idx];
while(idx < kfs->count - 1 && to > kfs->times[idx + 1]) {
memcpy(&kfs->values[idx], &kfs->values[idx + 1], sizeof(*kfs->values));
memcpy(&kfs->times[idx], &kfs->times[idx + 1], sizeof(*kfs->times));
idx++;
}
while(idx > 0 && to < kfs->times[idx - 1]) {
memcpy(&kfs->values[idx], &kfs->values[idx - 1], sizeof(*kfs->values));
memcpy(&kfs->times[idx], &kfs->times[idx - 1], sizeof(*kfs->times));
idx--;
}
kfs->times[idx] = to;
kfs->values[idx] = val;
return idx;
}
CUTIVIS size_t CHi_MoveKeyframeBy(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx, float dt) {
return CHi_MoveKeyframe(ng, kfs, idx, kfs->times[idx] + dt);
}
CUTIVIS void CHi_DeleteKeyframe(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx) {
memmove(&kfs->times[idx], &kfs->times[idx + 1], (kfs->count - idx - 1) * sizeof(*kfs->times));
memmove(&kfs->values[idx], &kfs->values[idx + 1], (kfs->count - idx - 1) * sizeof(*kfs->values));
kfs->count--;
}
CUTIVIS size_t CHi_GetClosestKeyframe(CHiNodeGraph *ng, size_t kfsIdx, float t) {
CHiKeyframes *kfs = ng->keyframesList.keyframes[kfsIdx];
if(kfs->count == 1) {
return 0;
}
size_t idx = bisect(&t, kfs->times, kfs->count, sizeof(*kfs->times), float_compar);
if(idx == 0) {
return idx;
}
if(idx == kfs->count) {
return kfs->count - 1;
}
if(fabs(kfs->times[idx] - t) < fabs(kfs->times[idx - 1] - t)) {
return idx;
} else {
return idx - 1;
}
}
CUTIVIS void CHi_SetExtrapolationMode(CHiNodeGraph *ng, CHiPubNode *n, size_t sinkIdx, CHiExtrapolationMode mode, float* params) {
if(n->sinks[sinkIdx].type != CUTIHI_VAL_KEYED) {
return;
}
CHiKeyframes *kfs = n->sinks[sinkIdx].data.keyed;
kfs->extrapolationMode = mode;
memcpy(kfs->extrapolationParameter, params, sizeof(kfs->extrapolationParameter));
}
CUTIVIS void CHi_SetDuration(CHiNodeGraph *ng, float d) {
ng->duration = d;
}
CUTIVIS int CHi_Hysteresis(CHiPubNode *root) {
if(root->ng->compilationStatus != CUTIHI_COMP_READY) return 0;
for(size_t s = 0; s < root->sinkCount; s++) {
if(root->sinks[s].type == CUTIHI_VAL_LINKED) {
CHi_Hysteresis(root->sinks[s].data.linked.to);
}
}
root->Perform(root);
return 1;
}
bool timespec_less(const struct timespec l, const struct timespec r) {
if(l.tv_sec == r.tv_sec) {
return l.tv_nsec < r.tv_nsec;
} else {
return l.tv_sec < r.tv_sec;
}
}
struct timespec timespec_sub(const struct timespec l, const struct timespec r) {
struct timespec ret;
ret.tv_sec = l.tv_sec - r.tv_sec;
ret.tv_nsec = l.tv_nsec - r.tv_nsec;
if(ret.tv_nsec < 0) {
ret.tv_nsec += 1000000000L;
ret.tv_sec--;
}
return ret;
}
struct timespec timespec_addf(const struct timespec l, const float r) {
struct timespec ret;
ret.tv_sec = l.tv_sec + floorf(r);
ret.tv_nsec = l.tv_nsec + (r - floorf(r)) * 1000000000L;
if(ret.tv_nsec > 1000000000L) {
ret.tv_sec++;
ret.tv_nsec -= 1000000000L;
}
return ret;
}
struct timespec timespec_add(const struct timespec l, const struct timespec r) {
struct timespec ret;
ret.tv_sec = l.tv_sec + r.tv_sec;
ret.tv_nsec = l.tv_nsec + r.tv_nsec;
if(ret.tv_nsec > 1000000000L) {
ret.tv_nsec -= 1000000000L;
ret.tv_sec++;
}
return ret;
}
float timespecToFloat(const struct timespec t) {
return t.tv_sec + t.tv_nsec / 1000000000.f;
}
struct CompileCtx {
CHiNodeGraph *ng;
};
void *compile_thread(void *ctx_) {
struct CompileCtx *ctx = ctx_;
ctx->ng->time = ctx->ng->timedelta = 0;
puts("START");
for(size_t nIdx = 0; nIdx < ctx->ng->count; nIdx++) {
if(ctx->ng->nodes[nIdx]->Start) {
ctx->ng->nodes[nIdx]->Start(ctx->ng->nodes[nIdx]);
} else {
ctx->ng->nodes[nIdx]->Perform(ctx->ng->nodes[nIdx]);
}
}
if(CHi_GetMode() == CUTIHI_MODE_LIVE) {
struct timespec start;
clock_gettime(CLOCK_MONOTONIC, &start);
struct timespec finish = timespec_addf(start, ctx->ng->duration);
for(size_t frm = 0; ctx->ng->compilationStatus != CUTIHI_COMP_KILL_YOURSELF; frm++) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if(ctx->ng->duration != -1 && timespec_less(finish, now)) {
break;
}
struct timespec end = timespec_addf(now, 0.033333333333333333333333);
CHi_Time_Set(ctx->ng, timespecToFloat(timespec_sub(now, start)));
for(size_t nIdx = 0; nIdx < ctx->ng->count; nIdx++) {
ctx->ng->nodes[nIdx]->Perform(ctx->ng->nodes[nIdx]);
}
if(ctx->ng->eventOnFrameComplete) {
ctx->ng->eventOnFrameComplete(ctx->ng);
}
do {
clock_gettime(CLOCK_MONOTONIC, &now);
} while(timespec_less(now, end));
}
} else {
__uint128_t diff;
for(uint64_t frm = 0; ctx->ng->compilationStatus != CUTIHI_COMP_KILL_YOURSELF && (ctx->ng->duration == -1 || frm < ctx->ng->duration * 30);) {
CHi_Time_Set(ctx->ng, frm / 30.f);
for(size_t nIdx = 0; nIdx < ctx->ng->count; nIdx++) {
ctx->ng->nodes[nIdx]->Perform(ctx->ng->nodes[nIdx]);
}
struct timespec last;
clock_gettime(CLOCK_MONOTONIC, &last);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
diff += timespec_sub(now, last).tv_nsec;
if(ctx->ng->eventOnFrameComplete) {
ctx->ng->eventOnFrameComplete(ctx->ng);
}
frm++;
}
}
for(size_t nIdx = 0; nIdx < ctx->ng->count; nIdx++) {
if(ctx->ng->nodes[nIdx]->Stop) {
ctx->ng->nodes[nIdx]->Stop(ctx->ng->nodes[nIdx]);
}
}
puts("END");
if(ctx->ng->eventOnStopComplete) {
ctx->ng->eventOnStopComplete(ctx->ng);
}
ctx->ng->compilationStatus = CUTIHI_COMP_READY;
free(ctx);
return NULL;
}
CUTIVIS void CHi_BeginCompilation(CHiNodeGraph *ng) {
ng->compilationStatus = CUTIHI_COMP_RUNNING;
struct CompileCtx *ctx = calloc(sizeof(*ctx), 1);
ctx->ng = ng;
pthread_t thrd;
pthread_create(&thrd, NULL, &compile_thread, ctx);
}
CUTIVIS void CHi_StopCompilation(CHiNodeGraph *ng) {
if(ng->compilationStatus == CUTIHI_COMP_RUNNING) {
ng->compilationStatus = CUTIHI_COMP_KILL_YOURSELF;
}
}
static int image_perform(CHiPubNode *node) {
if(node->clean) return 1;
node->sources->type = CUTIHI_VAL_SAMPLE;
if(node->sources->data.sample) CHi_Image_Free(node->sources->data.sample);
struct sail_image *simg;
SAIL_TRY(sail_load_from_file(node->sinks[0].data.text, &simg));
struct sail_image *cimg;
sail_convert_image(simg, SAIL_PIXEL_FORMAT_BPP64_BGRA, &cimg);
sail_destroy_image(simg);
CHiImage *img = CHi_Image_New(2, 4, (cimg->bytes_per_line + 15) & ~15, cimg->width, cimg->height, NULL);
CHi_Restride(cimg->pixels, img->data16, cimg->bytes_per_line, img->stride, img->height);
node->sources->data.sample = img;
for(size_t y = 0; y < img->height; y++) {
for(size_t x = 0; x < img->stride; x += 16) {
__m128i pixels = _mm_load_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x));
pixels = apply_gamma_epi16(pixels, _mm_set_ps(1.0f, 2.2f, 2.2f, 2.2f));
_mm_stream_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x), pixels);
}
}
sail_destroy_image(cimg);
node->clean = 0;
return 1;
err:
node->sources->data.sample = NULL;
return 0;
}
CUTIVIS CHiPubNode *CHi_Image() {
CHiPubNode *n = malloc(sizeof(*n));
n->type = CUTIHI_T('CIma','ge ');
n->Start = n->Stop = NULL;
n->Perform = image_perform;
n->clean = 0;
n->sinkCount = 1;
n->sinks = calloc(sizeof(*n->sinks), 1);
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), 1);
return n;
}
static int embed_perform(CHiPubNode *node) {
if(node->clean) return 1;
node->sources[0].type = CUTIHI_VAL_SAMPLE;
CHiImage *main = CHi_Crawl(&node->sinks[0])->data.sample;
if(node->sources->data.sample) CHi_Image_Free(node->sources->data.sample);
CHiImage *dest = node->sources->data.sample = CHi_Image_New(2, 4, main->stride, main->width, main->height, NULL);
memcpy(dest->data16, main->data16, main->stride * main->height);
for(int sid = 0; sid < CUTIHI_EMBED_MAX_SMALLS; sid++) {
CHiImage *sub = CHi_Crawl(&node->sinks[1 + sid * 3])->data.sample;
if(!sub) continue;
int sy = 0;
int dy = (int16_t) CHi_Crawl(&node->sinks[2 + sid * 3])->data.vec4[1];
if(dy < 0) {
sy = -dy;
dy = 0;
}
for(; sy < sub->height && dy < dest->height; sy++, dy++) {
int sx = 0;
int dx = (int16_t) CHi_Crawl(&node->sinks[2 + sid * 3])->data.vec4[0];
if(dx < 0) {
sx = -dx;
dx = 0;
}
for(; sx < sub->width && dx < dest->width; sx += 2, dx += 2) {
__m128i bottom = _mm_loadu_si128((__m128i*) ((uintptr_t) dest->data16 + dy * dest->stride + dx * 8));
__m128i top = _mm_loadu_si128((__m128i*) ((uintptr_t) sub->data16 + sy * sub->stride + sx * 8));
__m128i alpha = _mm_shuffle_epi8(top, _mm_set_epi8(15, 14, 15, 14, 15, 14, 15, 14, 7, 6, 7, 6, 7, 6, 7, 6));
__m128i invAlpha = _mm_sub_epi16(_mm_set1_epi16(0xFFFF), alpha);
__m128i result = _mm_add_epi16(_mm_mulhi_epu16(top, alpha), _mm_mulhi_epu16(bottom, invAlpha));
_mm_storeu_si128((__m128i*) ((uintptr_t) dest->data16 + dy * dest->stride + dx * 8), result);
}
}
}
node->clean = 0;
return 1;
}
CUTIVIS CHiPubNode *CHi_Embed() {
CHiPubNode *n = malloc(sizeof(*n));
n->type = CUTIHI_T('CEmb','ed ');
n->Start = n->Stop = NULL;
n->Perform = embed_perform;
n->clean = 0;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount = 1 + 3 * CUTIHI_EMBED_MAX_SMALLS);
for(int i = 0; i < CUTIHI_EMBED_MAX_SMALLS; i++) {
n->sinks[2 + i * 3].type = CUTIHI_VAL_VEC4;
n->sinks[2 + i * 3].data.vec4[0] = 0;
n->sinks[2 + i * 3].data.vec4[1] = 0;
n->sinks[3 + i * 3].type = CUTIHI_VAL_VEC4;
n->sinks[3 + i * 3].data.vec4[0] = 1;
}
n->sources = calloc(sizeof(*n->sources), n->sourceCount = 1);
return n;
}
static int constantsample_perform(CHiPubNode *node) {
if(node->clean) return 1;
node->sources[0].type = CUTIHI_VAL_SAMPLE;
if(node->sources->data.sample) CHi_Image_Free(node->sources->data.sample);
CHiValue *sink = CHi_Crawl(&node->sinks[0]);
CHiImage *img = CHi_Image_New(2, 4, 8 * 16, 16, 16, NULL);
for(int i = 0; i < 256; i++) {
img->data16[i * 4 + 0] = sink->data.vec4[2] * 65535;
img->data16[i * 4 + 1] = sink->data.vec4[1] * 65535;
img->data16[i * 4 + 2] = sink->data.vec4[0] * 65535;
img->data16[i * 4 + 3] = 65535;
}
node->sources->data.sample = img;
node->clean = 0;
return 1;
}
CUTIVIS CHiPubNode *CHi_ConstantSample() {
CHiPubNode *n = malloc(sizeof(*n));
n->type = CUTIHI_T('CCns','tCol');
n->Start = n->Stop = NULL;
n->Perform = constantsample_perform;
n->clean = 0;
n->sinkCount = 1;
n->sinks = calloc(sizeof(*n->sinks), 1);
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), 1);
return n;
}
static int modulate_perform(CHiPubNode *node) {
if(node->clean) return 1;
node->sources[0].type = CUTIHI_VAL_SAMPLE;
if(node->sources->data.sample) CHi_Image_Free(node->sources->data.sample);
node->sources->data.sample = CHi_Image_New(2, 4, 8 * 16, 16, 16, NULL);
node->clean = 0;
return 1;
}
CUTIVIS CHiPubNode *CHi_Modulate() {
CHiPubNode *n = malloc(sizeof(*n));
n->type = CUTIHI_T('CMod','ulat');
n->Start = n->Stop = NULL;
n->Perform = modulate_perform;
n->clean = 0;
n->sinkCount = 4;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount);
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), n->sourceCount);
return n;
}
static void update_keyed_values(CHiNodeGraph *ng) {
for(size_t kfsIdx = 0; kfsIdx < ng->keyframesList.count; kfsIdx++) {
CHiKeyframes *kfs = ng->keyframesList.keyframes[kfsIdx];
kfs->current.type = kfs->type;
float now = ng->time;
size_t idx = bisect(&now, kfs->times, kfs->count, sizeof(now), float_compar);
if(idx == 0) {
kfs->current.data = kfs->values[idx];
if(kfs->current.type == CUTIHI_VAL_VEC4 && kfs->extrapolationMode == CUTIHI_EXTRAPOLATION_CONSTANT) {
kfs->current.data.vec4[0] += (now - kfs->times[0]) * kfs->extrapolationParameter[0];
kfs->current.data.vec4[1] += (now - kfs->times[0]) * kfs->extrapolationParameter[1];
kfs->current.data.vec4[2] += (now - kfs->times[0]) * kfs->extrapolationParameter[2];
kfs->current.data.vec4[3] += (now - kfs->times[0]) * kfs->extrapolationParameter[3];
}
} else if(idx == kfs->count) {
kfs->current.data = kfs->values[idx - 1];
if(kfs->current.type == CUTIHI_VAL_VEC4 && kfs->extrapolationMode == CUTIHI_EXTRAPOLATION_CONSTANT) {
kfs->current.data.vec4[0] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[0];
kfs->current.data.vec4[1] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[1];
kfs->current.data.vec4[2] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[2];
kfs->current.data.vec4[3] += (now - kfs->times[kfs->count - 1]) * kfs->extrapolationParameter[3];
}
} else {
if(kfs->type == CUTIHI_VAL_VEC4) {
float alpha = (now - kfs->times[idx - 1]) / (kfs->times[idx] - kfs->times[idx - 1]);
kfs->current.data.vec4[0] = kfs->values[idx - 1].vec4[0] + (kfs->values[idx].vec4[0] - kfs->values[idx - 1].vec4[0]) * alpha;
kfs->current.data.vec4[1] = kfs->values[idx - 1].vec4[1] + (kfs->values[idx].vec4[1] - kfs->values[idx - 1].vec4[1]) * alpha;
kfs->current.data.vec4[2] = kfs->values[idx - 1].vec4[2] + (kfs->values[idx].vec4[2] - kfs->values[idx - 1].vec4[2]) * alpha;
kfs->current.data.vec4[3] = kfs->values[idx - 1].vec4[3] + (kfs->values[idx].vec4[3] - kfs->values[idx - 1].vec4[3]) * alpha;
} else {
kfs->current.data = kfs->values[idx - 1];
}
}
}
}
static int time_perform(CHiPubNode *node) {
node->sources->type = CUTIHI_VAL_VEC4;
node->sources->data.vec4[0] = node->ng->time;
node->clean = 0;
return 1;
}
CUTIVIS void CHi_Time_Set(CHiNodeGraph *ng, float f) {
ng->timedelta = f - ng->time;
ng->time = f;
update_keyed_values(ng);
}
CUTIVIS float CHi_Time_Get(CHiNodeGraph *ng) {
return ng->time;
}
CUTIVIS float CHi_Time_GetDelta(CHiNodeGraph *ng) {
return ng->timedelta;
}
CUTIVIS CHiPubNode *CHi_Time() {
CHiPubNode *n = malloc(sizeof(*n));
n->type = CUTIHI_T('CTim','e ');
n->Start = n->Stop = NULL;
n->Perform = time_perform;
n->clean = 0;
n->sinkCount = 0;
n->sinks = NULL;
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), 1);
return n;
}
static PangoFontMap *pfontmap;
static PangoContext *pcontext;
static PangoFontDescription * pfontdesc;
static PangoLayout *playout;
static int text_perform(CHiPubNode *n) {
if(n->clean) return 1;
if(!pfontmap) {
pfontmap = pango_ft2_font_map_new();
pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(pfontmap), 72, 72);
pcontext = pango_font_map_create_context(pfontmap);
pango_context_set_language(pcontext, pango_language_from_string("en_US"));
pango_context_set_base_dir(pcontext, PANGO_DIRECTION_LTR);
pfontdesc = pango_font_description_from_string("Open Sans 48");
playout = pango_layout_new(pcontext);
pango_layout_set_font_description(playout, pfontdesc);
}
pango_layout_set_markup(playout, CHi_Crawl(&n->sinks[0])->data.text, -1);
pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(pfontmap), CHi_Crawl(&n->sinks[2])->data.vec4[0], CHi_Crawl(&n->sinks[2])->data.vec4[0]);
PangoRectangle extents;
pango_layout_get_extents(playout, NULL, &extents);
n->sources[0].type = CUTIHI_VAL_SAMPLE;
if(n->sources->data.sample) CHi_Image_Free(n->sources->data.sample);
size_t width = (PANGO_PIXELS(extents.width) + 15) & ~15;
CHiImage *chiret = CHi_Image_New(2, 4, 8 * width, width, PANGO_PIXELS(extents.height), NULL);
n->sources->data.sample = chiret;
FT_Bitmap bmp = {};
FT_Bitmap_New(&bmp);
bmp.width = chiret->width;
bmp.rows = chiret->height;
bmp.buffer = calloc(bmp.width, bmp.rows);
bmp.pitch = chiret->width;
bmp.pixel_mode = FT_PIXEL_MODE_GRAY;
bmp.num_grays = 256;
pango_ft2_render_layout(&bmp, playout, PANGO_PIXELS(extents.x) + (PANGO_PIXELS(extents.width) + 15) % 16 / 4, PANGO_PIXELS(extents.y));
__m128i ones = _mm_set1_epi64x(
(((size_t) (n->sinks[1].data.vec4[2] * 255) % 256) << 0) |
(((size_t) (n->sinks[1].data.vec4[1] * 255) % 256) << 16) |
(((size_t) (n->sinks[1].data.vec4[0] * 255) % 256) << 32) |
0x0100000000000000
);
for(size_t p = 0; p < bmp.width * bmp.rows; p += 2) {
__m128i alphad0 =
_mm_mullo_epi16(ones, _mm_set_epi16(bmp.buffer[p + 1], bmp.buffer[p + 1], bmp.buffer[p + 1], bmp.buffer[p + 1], bmp.buffer[p + 0], bmp.buffer[p + 0], bmp.buffer[p + 0], bmp.buffer[p + 0]));
_mm_stream_si128((__m128i*) &chiret->data16[p * 4], alphad0);
}
free(bmp.buffer);
n->clean = 0;
return 1;
}
CUTIVIS CHiPubNode *CHi_Text() {
CHiPubNode *n = malloc(sizeof(*n));
n->type = CUTIHI_T('CTex','t ');
n->Start = n->Stop = NULL;
n->Perform = text_perform;
n->clean = 0;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount = 3);
n->sinks[2].type = CUTIHI_VAL_VEC4;
n->sinks[2].data.vec4[0] = 72;
n->sources = calloc(sizeof(*n->sources), n->sourceCount = 1);
return n;
}
static int mixer_perform(CHiPubNode *n) {
n->sources[0].type = CUTIHI_VAL_SAMPLE;
if(n->sources[0].data.sample) {
CHi_Image_Free(n->sources[0].data.sample);
n->sources[0].data.sample = NULL;
}
CHiImage *src0 = CHi_Crawl(&n->sinks[0])->data.sample;
CHiImage *src1 = CHi_Crawl(&n->sinks[1])->data.sample;
if(!src0 && !src1) {
return 1;
}
assert(src0->width == src1->width && src0->height == src1->height);
n->sources[0].data.sample = CHi_Image_New(2, 1, (src0->stride + 15) & ~15, src0->width, src0->height, NULL);
for(size_t b = 0; b < src0->stride; b += 16) {
__m128i a0 = src0 ? _mm_load_si128((__m128i*) ((uintptr_t) src0->data16 + b)) : _mm_setzero_si128();
__m128i a1 = src1 ? _mm_load_si128((__m128i*) ((uintptr_t) src1->data16 + b)) : _mm_setzero_si128();
_mm_stream_si128((__m128i*) ((uintptr_t) n->sources[0].data.sample->data16 + b), _mm_adds_epi16(a0, a1));
}
n->clean = 0;
return 1;
}
CUTIVIS CHiPubNode *CHi_Mixer() {
CHiPubNode *n = calloc(1, sizeof(*n));
n->type = CUTIHI_T('CMix','er ');
n->Start = n->Stop = NULL;
n->Perform = mixer_perform;
n->clean = 0;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount = 2);
n->sources = calloc(sizeof(*n->sources), n->sourceCount = 1);
return n;
}
static int preview_perform(CHiPubNode *n) {
return 1;
}
CUTIVIS CHiPubNode *CHi_Preview() {
CHiPubNode *n = malloc(sizeof(*n));
n->type = CUTIHI_T('CPre','view');
n->Start = n->Stop = NULL;
n->Perform = preview_perform;
n->clean = 0;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount = 1);
n->sinks[0].type = CUTIHI_VAL_SAMPLE;
n->sinks[0].data.sample = NULL;
n->sources = NULL;
n->sourceCount = 0;
return n;
}
static void save_chival(CHiNodeGraph *ng, CHiSaveWriter writer, CHiValType type, CHiValueRaw data, void *ud) {
if(type == CUTIHI_VAL_TEXT) {
size_t len = strlen(data.text);
writer(ud, &(uint32_t) {len}, sizeof(uint32_t));
writer(ud, data.text, len);
} else if(type == CUTIHI_VAL_VEC4) {
writer(ud, data.vec4, sizeof(data.vec4));
} else if(type == CUTIHI_VAL_LINKED) {
size_t index;
for(index = 0; index < ng->count; index++) {
if(ng->nodes[index] == data.linked.to) {
break;
}
}
assert(index < ng->count);
writer(ud, &(uint64_t) {index}, sizeof(uint64_t));
} else if(type == CUTIHI_VAL_KEYED) {
size_t index;
for(index = 0; index < ng->keyframesList.count; index++) {
if(ng->keyframesList.keyframes[index] == data.keyed) {
break;
}
}
assert(index < ng->count);
writer(ud, &(uint64_t) {index}, sizeof(uint64_t));
}
}
CUTIVIS int CHi_NodeGraphSave(CHiNodeGraph *ng, CHiSaveWriter writer, void *ud) {
writer(ud, "\x71\x74\xCE\xA0", 4);
writer(ud, &(float) {ng->duration}, sizeof(float));
writer(ud, &(float) {ng->time}, sizeof(float));
writer(ud, &(uint64_t) {ng->count}, sizeof(uint64_t));
for(size_t i = 0; i < ng->count; i++) {
CHiPubNode *node = ng->nodes[i];
writer(ud, &(uint16_t) {node->type}, sizeof(uint16_t));
node->Save(node, ud, writer);
writer(ud, &(uint16_t) {node->sinkCount}, sizeof(uint16_t));
for(size_t sink = 0; sink < node->sinkCount; sink++) {
writer(ud, &(uint16_t) {node->sinks[sink].type}, sizeof(uint16_t));
save_chival(ng, writer, node->sinks[sink].type, node->sinks[sink].data, ud);
}
}
writer(ud, &(uint64_t) {ng->keyframesList.count}, sizeof(uint64_t));
for(size_t i = 0; i < ng->keyframesList.count; i++) {
CHiKeyframes *kfs = ng->keyframesList.keyframes[i];
writer(ud, &(uint16_t) {kfs->type}, sizeof(uint16_t));
writer(ud, &(uint64_t) {kfs->count}, sizeof(uint64_t));
writer(ud, kfs->times, sizeof(*kfs->times) * kfs->count);
for(size_t k = 0; k < kfs->count; k++) {
save_chival(ng, writer, kfs->type, kfs->values[k], ud);
}
writer(ud, &(uint16_t) {kfs->extrapolationMode}, sizeof(uint16_t));
writer(ud, kfs->extrapolationParameter, sizeof(kfs->extrapolationParameter));
}
return 0;
}

268
hi/node.h Normal file
View File

@@ -0,0 +1,268 @@
#ifndef _NODE_H
#define _NODE_H
#include<stdint.h>
#include<stddef.h>
#include"defs.h"
#include"bs.h"
#include<arpa/inet.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CUTIHI_T(a, b) ((((uint64_t) htonl(b)) << 32) | htonl(a))
typedef size_t(*CHiSaveWriter)(void *ud, const void *data, size_t len);
typedef enum {
CUTIHI_VAL_NONE = 0,
CUTIHI_VAL_LINKED = 1,
CUTIHI_VAL_KEYED = 2,
CUTIHI_VAL_SAMPLE = 3,
CUTIHI_VAL_TEXT = 4,
CUTIHI_VAL_VEC4 = 5,
CUTIHI_VAL_WEAK_PTR = 6,
CUTIHI_VAL_VP9BS = 666,
CUTIHI_VAL_VP8BS = 667,
CUTIHI_VAL_OPUSBS = 668,
} CHiValType;
struct CHiImage;
struct CHiPubNode;
struct CHiKeyframes;
typedef union {
struct {
struct CHiPubNode *to;
uint8_t idx;
} linked;
struct CHiImage *sample;
char *text;
float vec4[4];
CHiBSFrames *bitstream;
struct CHiKeyframes* keyed;
uintptr_t weakptr;
} CHiValueRaw;
typedef struct {
CHiValType type;
CHiValueRaw data;
} CHiValue;
typedef struct CHiPubNode {
uint64_t type;
uint32_t flags;
#define CUTIHI_PERFORM_UPDATED 1
#define CUTIHI_PERFORM_NOTUPDATED 0
int (*Perform)(struct CHiPubNode *node);
int (*Start)(struct CHiPubNode *node);
int (*Stop)(struct CHiPubNode *node);
char clean;
int (*Save)(struct CHiPubNode *node, void *ud, CHiSaveWriter writer);
size_t sinkCount;
CHiValue *sinks;
size_t sourceCount;
CHiValue *sources;
struct CHiNodeGraph *ng;
char _dfsmark;
} CHiPubNode;
typedef enum {
CUTIHI_COMP_READY,
CUTIHI_COMP_RUNNING,
CUTIHI_COMP_KILL_YOURSELF
} CHiCompilationStatus;
typedef CHiPubNode *CHiAdjacency[2];
typedef enum {
CUTIHI_EXTRAPOLATION_NONE = 0, CUTIHI_EXTRAPOLATION_CONSTANT = 1
} CHiExtrapolationMode;
typedef struct CHiKeyframes {
CHiValType type;
size_t count;
float *times;
CHiValueRaw *values;
CHiValue current;
CHiPubNode *node;
CHiExtrapolationMode extrapolationMode;
float extrapolationParameter[4];
} CHiKeyframes;
typedef struct CHiKeyframesList {
size_t count;
CHiKeyframes **keyframes;
} CHiKeyframesList;
typedef struct CHiNodeGraph {
size_t capacity;
size_t count;
CHiPubNode **nodes;
float duration;
void *ud;
void(*eventOnStopComplete)(struct CHiNodeGraph*);
void(*eventOnFrameComplete)(struct CHiNodeGraph*);
CHiCompilationStatus compilationStatus;
size_t adjacencyCount, adjacencyCapacity;
CHiAdjacency *adjacencies;
CHiKeyframesList keyframesList;
float time;
float timedelta;
} CHiNodeGraph;
CUTIVIS CHiNodeGraph *CHi_NewNodeGraph();
CUTIVIS int CHi_NodeGraphSave(CHiNodeGraph *ng, CHiSaveWriter writer, void *ud);
CUTIVIS void CHi_RegisterNode(CHiNodeGraph*, CHiPubNode*);
CUTIVIS void CHi_MakeDirty(CHiNodeGraph*, CHiPubNode*);
CUTIVIS int CHi_ConfigureSink(CHiPubNode*, size_t, CHiValue);
CUTIVIS void CHi_MakeKeyframe(CHiNodeGraph *ng, CHiPubNode *n, size_t idx);
CUTIVIS size_t CHi_MoveKeyframe(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx, float to);
CUTIVIS size_t CHi_MoveKeyframeBy(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx, float dt);
CUTIVIS size_t CHi_GetClosestKeyframe(CHiNodeGraph *ng, size_t kfsIdx, float t);
CUTIVIS void CHi_SetExtrapolationMode(CHiNodeGraph *ng, CHiPubNode *n, size_t sinkIdx, CHiExtrapolationMode mode, float* params);
CUTIVIS void CHi_DeleteKeyframe(CHiNodeGraph *ng, CHiKeyframes *kfs, size_t idx);
CUTIVIS int CHi_Hysteresis(CHiPubNode*);
CUTIVIS void CHi_SetDuration(CHiNodeGraph *ng, float);
CUTIVIS void CHi_BeginCompilation(CHiNodeGraph *ng);
CUTIVIS void CHi_StopCompilation(CHiNodeGraph *ng);
#define CUTIHI_NODE_IMAGE 0
#define CUTIHI_IMAGE_IN_FILE 0
#define CUTIHI_IMAGE_OUT_SAMPLE 1
CUTIVIS CHiPubNode *CHi_Image();
#define CUTIHI_NODE_EMBED 1
#define CUTIHI_EMBED_OUT_MAIN 0
#define CUTIHI_EMBED_MAX_SMALLS 3
CUTIVIS CHiPubNode *CHi_Embed();
#define CUTIHI_NODE_CONSTANTSAMPLE 2
#define CUTIHI_CONSTANTSAMPLE_IN_COLOR 0
#define CUTIHI_CONSTANTSAMPLE_OUT_SAMPLE 1
CUTIVIS CHiPubNode *CHi_ConstantSample();
#define CUTIHI_NODE_MODULATE 3
#define CUTIHI_MODULATE_IN_SAMPLE 0
#define CUTIHI_MODULATE_BRIGHTNESS 1
#define CUTIHI_MODULATE_CONTAST 2
#define CUTIHI_MODULATE_HUE 3
#define CUTIHI_MODULATE_OUT_SAMPLE 4
CUTIVIS CHiPubNode *CHi_Modulate();
#define CUTIHI_NODE_MOVIE 4
#define CUTIHI_MOVIE_IN_FILE 0
#define CUTIHI_MOVIE_IN_TIME 1
#define CUTIHI_MOVIE_IN_DELAY 2
#define CUTIHI_MOVIE_OUT_SAMPLE 2
CUTIVIS CHiPubNode *CHi_Movie();
#define CUTIHI_NODE_TIME 5
#define CUTIHI_TIME_OUT 0
CUTIVIS void CHi_Time_Set(CHiNodeGraph *ng, float);
CUTIVIS float CHi_Time_Get(CHiNodeGraph *ng);
CUTIVIS float CHi_Time_GetDelta(CHiNodeGraph *ng);
CUTIVIS CHiPubNode *CHi_Time();
#define CUTIHI_NODE_ENCODEVP9 6
#define CUTIHI_ENCODEVP9_IN_SAMPLE 0
#define CUTIHI_ENCODEVP9_IN_BITRATE 1
#define CUTIHI_ENCODEVP9_IN_BITRATE 1
#define CUTIHI_ENCODEVP9_OUT_BITSTREAM 0
#define CUTIHI_NODE_ENCODEVP8 17
CUTIVIS CHiPubNode *CHi_EncodeVP9();
CUTIVIS CHiPubNode *CHi_EncodeVP8();
CUTIVIS int CHi_EncodeVP9_Start(CHiPubNode*);
CUTIVIS int CHi_EncodeVP9_Stop(CHiPubNode*);
#define CUTIHI_NODE_MUXWEBM 7
#define CUTIHI_MUXWEBM_IN_BITSTREAM 0
#define CUTIHI_MUXWEBM_IN_AUDIOBITSTREAM 1
#define CUTIHI_MUXWEBM_IN_FILENAME 2
CUTIVIS CHiPubNode *CHi_MuxWebm();
CUTIVIS int CHi_MuxWebm_Start(CHiPubNode*);
CUTIVIS int CHi_MuxWebm_Stop(CHiPubNode*);
#define CUTIHI_NODE_WINDOW 8
#define CUTIHI_WINDOW_IN_WINDOWNAME 0
#define CUTIHI_WINDOW_OUT_SAMPLE 1
CUTIVIS CHiPubNode *CHi_Window();
CUTIVIS size_t CHi_Window_GetSourceCount();
CUTIVIS const char *CHi_Window_GetSourceName(size_t);
CUTIVIS uintptr_t CHi_Window_GetSourceData(size_t);
CUTIVIS size_t CHi_Window_GetNextSource(size_t);
#define CUTIHI_NODE_MICROPHONE 9
#define CUTIHI_MICROPHONE_IN_NAME 0
#define CUTIHI_MICROPHONE_OUT_AUDIO 1
CUTIVIS CHiPubNode *CHi_Microphone();
#define CUTIHI_NODE_TEXT 10
#define CUTIHI_TEXT_IN_TEXT 0
#define CUTIHI_TEXT_OUT_SAMPLE 0
CUTIVIS CHiPubNode *CHi_Text();
#define CUTIHI_NODE_EXPORTWAV 11
CUTIVIS CHiPubNode *CHi_ExportWav();
CUTIVIS int CHi_ExportWav_Start(CHiPubNode*);
CUTIVIS int CHi_ExportWav_Stop(CHiPubNode*);
#define CUTIHI_NODE_ENCODEOPUS 12
CUTIVIS CHiPubNode *CHi_EncodeOpus();
CUTIVIS int CHi_EncodeOpus_Start(CHiPubNode*);
CUTIVIS int CHi_EncodeOpus_Stop(CHiPubNode*);
#define CUTIHI_NODE_CAMERA 13
CUTIVIS CHiPubNode *CHi_Camera();
#define CUTIHI_NODE_PREVIEW 14
CUTIVIS CHiPubNode *CHi_Preview();
#define CUTIHI_NODE_COMPONENT_SCALE 15
CUTIVIS CHiPubNode *CHi_ComponentScale();
#define CUTIHI_NODE_KEYHOOK 16
CUTIVIS CHiPubNode *CHi_Keyhook();
#define CUTIHI_NODE_MIXER 18
CUTIVIS CHiPubNode *CHi_Mixer();
CUTIVIS CHiValue *CHi_Crawl(CHiValue*);
CUTIVIS void CHi_Save(CHiNodeGraph *ng);
#ifdef __cplusplus
}
#endif
#endif

85
hi/opus.c Normal file
View File

@@ -0,0 +1,85 @@
#include"node.h"
#include<opus.h>
#include<string.h>
#include"img.h"
#include<stdlib.h>
#include<stdio.h>
struct CHiEncodeOpusNode {
CHiPubNode pubn;
size_t pcmSamples, pcmCapacity;
int16_t *pcmbuf;
size_t timestamp;
OpusEncoder *enc;
};
static int encodeopus_perform(CHiPubNode *pubn) {
struct CHiEncodeOpusNode *n = (struct CHiEncodeOpusNode*) pubn;
CHiImage *newpcm = CHi_Crawl(&pubn->sinks[0])->data.sample;
if(n->pcmSamples + newpcm->width > n->pcmCapacity) {
n->pcmbuf = realloc(n->pcmbuf, sizeof(*n->pcmbuf) * (n->pcmCapacity = ((n->pcmSamples + newpcm->width + 1023) & ~1023)));
}
memcpy(&n->pcmbuf[n->pcmSamples], newpcm->data16, sizeof(*n->pcmbuf) * newpcm->width);
n->pcmSamples += newpcm->width;
CHiBSFrames *frames = malloc(sizeof(*frames));
frames->count = 0;
size_t samp;
for(samp = 0; samp + 960 <= n->pcmSamples; samp += 960) {
frames = realloc(frames, sizeof(*frames) + sizeof(*frames->data) * (frames->count + 1));
frames->data[frames->count].timestamp = n->timestamp;
frames->data[frames->count].ptr = malloc(1276);
frames->data[frames->count].sz = opus_encode(n->enc, &n->pcmbuf[samp], 960, frames->data[frames->count].ptr, 1276);
if(frames->data[frames->count].sz < 0) puts("OPUS ERR");
frames->count++;
n->timestamp += 20;
}
memmove(n->pcmbuf, n->pcmbuf + samp, sizeof(*n->pcmbuf) * (n->pcmSamples - samp));
n->pcmSamples -= samp;
pubn->sources[0].type = CUTIHI_VAL_OPUSBS;
pubn->sources[0].data.bitstream = frames;
return 1;
}
CUTIVIS CHiPubNode *CHi_EncodeOpus() {
struct CHiEncodeOpusNode *ret = malloc(sizeof(*ret));
ret->pubn.type = CUTIHI_T('CEnc','Opus');
ret->pubn.Start = CHi_EncodeOpus_Start;
ret->pubn.Perform = &encodeopus_perform;
ret->pubn.Stop = CHi_EncodeOpus_Stop;
ret->pubn.clean = 0;
ret->pubn.sinks = calloc(sizeof(*ret->pubn.sinks), ret->pubn.sinkCount = 1);
ret->pubn.sources = calloc(sizeof(*ret->pubn.sources), ret->pubn.sourceCount = 1);
return &ret->pubn;
}
CUTIVIS int CHi_EncodeOpus_Start(CHiPubNode *pubn) {
struct CHiEncodeOpusNode *n = (struct CHiEncodeOpusNode*) pubn;
int error;
n->enc = opus_encoder_create(48000, 1, OPUS_APPLICATION_AUDIO, &error);
n->pcmSamples = 0;
n->pcmCapacity = 1024;
n->pcmbuf = malloc(sizeof(*n->pcmbuf) * n->pcmCapacity);
n->timestamp = 0;
return 1;
}
CUTIVIS int CHi_EncodeOpus_Stop(CHiPubNode *pubn) {
struct CHiEncodeOpusNode *n = (struct CHiEncodeOpusNode*) pubn;
opus_encoder_destroy(n->enc);
free(n->pcmbuf);
return 1;
}

132
hi/relay.c Normal file
View File

@@ -0,0 +1,132 @@
#include"relay.h"
#include"img.h"
#include<tmmintrin.h>
#include<X11/XKBlib.h>
#include<X11/extensions/record.h>
#include<stdio.h>
#include<pthread.h>
#include<stdatomic.h>
#include<math.h>
static int scale_perform(CHiPubNode *n) {
float *scales = CHi_Crawl(&n->sinks[0])->data.vec4;
CHiImage *img = CHi_Crawl(&n->sinks[1])->data.sample;
if(n->sources[0].data.sample) {
CHi_Image_Free(n->sources[0].data.sample);
}
CHiImage *ret = n->sources[0].data.sample = CHi_Image_New(img->bpc, img->channels, img->stride, img->width, img->height, NULL);
__m128i iscales = _mm_set_epi16(
scales[3] * 65535, scales[0] * 65535, scales[1] * 65535, scales[2] * 65535,
scales[3] * 65535, scales[0] * 65535, scales[1] * 65535, scales[2] * 65535
);
for(size_t y = 0; y < img->height; y++) {
for(size_t x = 0; x < img->width; x += 16) {
__m128i pixels8 = _mm_loadu_si128((__m128i*) ((uintptr_t) img->data16 + y * img->stride + x));
__m128i mulled = _mm_mulhi_epu16(pixels8, iscales);
_mm_storeu_si128((__m128i*) ((uintptr_t) ret->data16 + y * img->stride + x), mulled);
}
}
return 1;
}
CUTIVIS CHiPubNode *CHi_ComponentScale() {
CHiPubNode *n = malloc(sizeof(*n));
n->type = CUTIHI_T('CCmp','nScl');
n->Start = n->Stop = NULL;
n->Perform = scale_perform;
n->clean = 0;
n->sinkCount = 2;
n->sinks = calloc(sizeof(*n->sinks), n->sinkCount);
n->sourceCount = 1;
n->sources = calloc(sizeof(*n->sources), n->sourceCount);
return n;
}
static Display *dpy;
typedef struct {
CHiPubNode pub;
XRecordContext rc;
pthread_t thrd;
atomic_int key;
atomic_bool on;
} CHiKeyhookNode;
static void keyhook_handler(XPointer ud, XRecordInterceptData *recdata) {
if(recdata->category != XRecordFromServer) {
return;
}
int type = ((unsigned char*) recdata->data)[0] & 0x7F;
int key = ((unsigned char*) recdata->data)[1];
int repeat = recdata->data[2] & 1;
CHiKeyhookNode *n = (CHiKeyhookNode*) ud;
printf("%i\n", key);
if(!repeat && key == n->key) {
if(type == KeyPress) {
n->on = 1;
} else if(type == KeyRelease) {
n->on = 0;
}
}
XRecordFreeData(recdata);
}
static void *keyhook_thread(void *ud) {
CHiKeyhookNode *n = ud;
XRecordRange *rr = XRecordAllocRange();
rr->device_events.first = KeyPress;
rr->device_events.last = ButtonRelease;
n->rc = XRecordCreateContext(dpy, 0, &(XRecordClientSpec) {XRecordAllClients}, 1, &rr, 1);
XRecordEnableContext(dpy, n->rc, keyhook_handler, (XPointer) n);
return NULL;
}
static int keyhook_perform(CHiPubNode *n) {
((CHiKeyhookNode*) n)->key = CHi_Crawl(&n->sinks[0])->data.vec4[0];
n->sources[0].type = CUTIHI_VAL_VEC4;
if(n->ng->compilationStatus == CUTIHI_COMP_READY || n->sinks[1].data.vec4[0] == 0) {
n->sources[0].data.vec4[0] = ((CHiKeyhookNode*) n)->on;
} else if(((CHiKeyhookNode*) n)->on) {
n->sources[0].data.vec4[0] = fminf(1, n->sources[0].data.vec4[0] + CHi_Time_GetDelta(n->ng) * n->sinks[1].data.vec4[0]);
} else {
n->sources[0].data.vec4[0] = fmaxf(0, n->sources[0].data.vec4[0] - CHi_Time_GetDelta(n->ng) * n->sinks[1].data.vec4[0]);
}
return 1;
}
CUTIVIS CHiPubNode *CHi_Keyhook() {
CHiKeyhookNode *n = malloc(sizeof(*n));
n->pub.type = CUTIHI_T('CKey','hook');
n->pub.Start = n->pub.Stop = NULL;
n->pub.Perform = keyhook_perform;
n->pub.clean = 0;
n->pub.sinkCount = 2;
n->pub.sinks = calloc(sizeof(*n->pub.sinks), n->pub.sinkCount);
n->pub.sourceCount = 1;
n->pub.sources = calloc(sizeof(*n->pub.sources), n->pub.sourceCount);
n->on = 0;
n->key = 0;
if(!dpy) {
dpy = XOpenDisplay(NULL);
}
pthread_create(&n->thrd, NULL, keyhook_thread, n);
return &n->pub;
}

14
hi/relay.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef _CUTIHI_RELAY_H
#define _CUTIHI_RELAY_H
#include"node.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif

43
hi/test.c Normal file
View File

@@ -0,0 +1,43 @@
#include"node.h"
#include<vips/vips.h>
int main() {
VIPS_INIT("Cuticle Test");
CHiPubNode *noed0 = CHi_ConstantSample();
noed0->values[0].type = CUTIHI_VAL_VEC4;
noed0->values[0].vec4[0] = 255;
noed0->values[0].vec4[1] = 0;
noed0->values[0].vec4[2] = 0;
noed0->values[0].vec4[3] = 255;
CHiPubNode *noed1 = CHi_ConstantSample();
noed1->values[0].type = CUTIHI_VAL_VEC4;
noed1->values[0].vec4[0] = 0;
noed1->values[0].vec4[1] = 0;
noed1->values[0].vec4[2] = 255;
noed1->values[0].vec4[3] = 0;
CHiPubNode *noed2 = CHi_Embed();
noed2->values[1].type = CUTIHI_VAL_LINKED;
noed2->values[1].linked.to = noed0;
noed2->values[1].linked.idx = 1;
noed2->values[2].type = CUTIHI_VAL_VEC4;
noed2->values[2].vec4[0] = 0;
noed2->values[2].vec4[1] = 0;
noed2->values[2].vec4[2] = 1;
noed2->values[2].vec4[3] = 1;
noed2->values[3].type = CUTIHI_VAL_LINKED;
noed2->values[3].linked.to = noed1;
noed2->values[3].linked.idx = 1;
noed2->values[4].type = CUTIHI_VAL_VEC4;
noed2->values[4].vec4[0] = 0;
noed2->values[4].vec4[1] = 0;
noed2->values[4].vec4[2] = 1;
noed2->values[4].vec4[3] = 1;
noed2->Perform(noed2);
vips_pngsave((VipsImage*) noed2->values[0].sample, "test.png", NULL);
}

137
hi/webcam.c Normal file
View File

@@ -0,0 +1,137 @@
#include"node.h"
#include<sys/ioctl.h>
#include<sys/time.h>
#include<sys/mman.h>
#include<stdlib.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<time.h>
#include<linux/videodev2.h>
#include<libv4l2.h>
#include<errno.h>
#include"img.h"
#include<assert.h>
#include<smmintrin.h>
static int camId = -1;
static struct Buf {
size_t length;
uint8_t *ptr;
} bufs[2];
struct v4l2_format fmt;
static void xioctl(int fh, int request, void *arg) {
int r;
do {
r = v4l2_ioctl(fh, request, arg);
} while(r == -1 && ((errno == EINTR) || (errno == EAGAIN)));
if(r == -1) {
fprintf(stderr, "error %d, %s\\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
static int camera_perform(CHiPubNode *pubn) {
pubn->sources[0].type = CUTIHI_VAL_SAMPLE;
fd_set fds;
FD_ZERO(&fds);
FD_SET(camId, &fds);
int r = select(camId + 1, &fds, NULL, NULL, &(struct timeval) {.tv_sec = 0, .tv_usec = 100});
if(r == -1) {
return 1;
}
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
xioctl(camId, VIDIOC_DQBUF, &buf);
CHiImage *ret;
if(pubn->sources[0].data.sample) {
ret = pubn->sources[0].data.sample;
} else {
ret = CHi_Image_New(2, 4, 8 * ((fmt.fmt.pix.width + 15) & ~15), fmt.fmt.pix.width, fmt.fmt.pix.height, NULL);
pubn->sources[0].data.sample = ret;
}
/* 24-to-32 TODO: make faster by processing 48-to-64 bytes in one iteration (like bgra32torgb24 does) */
for(size_t y = 0; y < ret->height; y++) {
for(size_t x = 0; x < ret->width; x += 2) {
__m128i asdf = _mm_loadu_si128((__m128i*) &bufs[buf.index].ptr[(y * ret->width + x) * 3]);
asdf = _mm_shuffle_epi8(asdf, _mm_set_epi8(-128, -128, 5, -128, 4, -128, 3, -128, -128, -128, 2, -128, 1, -128, 0, -128));
asdf = _mm_or_si128(asdf, _mm_set_epi32(0xFFFF0000, 0, 0xFFFF0000, 0));
_mm_stream_si128((__m128i*) ((uintptr_t) ret->data16 + y * ret->stride + x * 8), asdf);
}
}
xioctl(camId, VIDIOC_QBUF, &buf);
return 1;
}
CUTIVIS CHiPubNode *CHi_Camera() {
if(camId == -1) {
camId = v4l2_open("/dev/video0", O_RDWR | O_NONBLOCK, 0);
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
xioctl(camId, VIDIOC_S_FMT, &fmt);
assert(fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24);
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof(req));
req.count = 2;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
xioctl(camId, VIDIOC_REQBUFS, &req);
for(int i = 0; i < 2; i++) {
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
xioctl(camId, VIDIOC_QUERYBUF, &buf);
bufs[i].length = buf.length;
bufs[i].ptr = v4l2_mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camId, buf.m.offset);
assert(MAP_FAILED != bufs[i].ptr);
}
for(int i = 0; i < 2; i++) {
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
xioctl(camId, VIDIOC_QBUF, &buf);
}
xioctl(camId, VIDIOC_STREAMON, &(enum v4l2_buf_type) {V4L2_BUF_TYPE_VIDEO_CAPTURE});
}
CHiPubNode *pubn = malloc(sizeof(*pubn));
pubn->type = CUTIHI_T('CWeb','Cam ');
pubn->clean = 0;
pubn->Start = pubn->Stop = NULL;
pubn->Perform = camera_perform;
pubn->sinks = calloc(sizeof(*pubn->sinks), pubn->sinkCount = 0);
pubn->sources = calloc(sizeof(*pubn->sources), pubn->sourceCount = 1);
pubn->ng = NULL;
return pubn;
}

418
hi/webmdec.cpp Normal file
View File

@@ -0,0 +1,418 @@
#include"node.h"
#include<stdlib.h>
#include<webm/webm_parser.h>
#include<webm/file_reader.h>
#include<vpx/vpx_decoder.h>
#include<vpx/vp8dx.h>
#include<assert.h>
#include<time.h>
#include"img.h"
#include<string.h>
#include<tmmintrin.h>
#include<smmintrin.h>
#include<opus.h>
#include<math.h>
#include"linearity.h"
struct CHiMovieNode;
struct CueParser : webm::Callback {
CHiMovieNode *node;
CueParser(CHiMovieNode *node) : node(node) {}
webm::Status OnInfo(const webm::ElementMetadata& metadata, const webm::Info& info) final override;
webm::Status OnTrackEntry(const webm::ElementMetadata &metadata, const webm::TrackEntry &info) override;
webm::Status OnSegmentBegin(const webm::ElementMetadata &metadata, webm::Action *action) override;
webm::Status OnCuePoint(const webm::ElementMetadata &metadata, const webm::CuePoint &cue) override;
};
struct AudioParser final : webm::Callback {
uint64_t audioTrack;
uint64_t currentClusterTimecode;
uint64_t untihl;
bool stop = true;
bool skip = false;
#define SAMPLE_ARR 48000
OpusDecoder *opus;
size_t sampleI = 0;
size_t sampleReadI = 0;
int16_t sampleArray[SAMPLE_ARR];
webm::Status OnClusterBegin(const webm::ElementMetadata &metadata, const webm::Cluster &cluster, webm::Action *action) final override {
currentClusterTimecode = cluster.timecode.value();
return webm::Status(webm::Status::kOkCompleted);
}
webm::Status OnBlockBegin(const webm::ElementMetadata &metadata, const webm::Block &block, webm::Action *action) final override {
if(block.track_number != audioTrack) {
skip = true;
*action = webm::Action::kSkip;
} else {
skip = false;
if(currentClusterTimecode + block.timecode >= untihl) {
stop = true;
} else {
stop = false;
}
}
return webm::Status(webm::Status::kOkCompleted);
}
webm::Status OnSimpleBlockBegin(const webm::ElementMetadata &metadata, const webm::SimpleBlock &block, webm::Action *action) final override {
return OnBlockBegin(metadata, block, action);
}
webm::Status OnFrame(const webm::FrameMetadata &metadata, webm::Reader *reader, uint64_t *bytes_remaining) final override {
uint8_t *data = new uint8_t[metadata.size];
uint64_t actuallyRead;
reader->Read(metadata.size, data, &actuallyRead);
if(!skip) {
int16_t *f = new int16_t[6400];
int numSamples = opus_decode(opus, data, metadata.size, f, 6400, 0);
if(numSamples >= 0) {
if(SAMPLE_ARR - sampleI >= (size_t) numSamples) {
memcpy(&sampleArray[sampleI], f, sizeof(*sampleArray) * numSamples);
sampleI = (sampleI + numSamples) % SAMPLE_ARR;
} else {
memcpy(&sampleArray[sampleI], f, sizeof(*sampleArray) * (SAMPLE_ARR - sampleI));
memcpy(sampleArray, &f[SAMPLE_ARR - sampleI], sizeof(*sampleArray) * (numSamples - SAMPLE_ARR + sampleI));
sampleI = (sampleI + numSamples) % SAMPLE_ARR;
}
}
delete[] f;
}
delete[] data;
*bytes_remaining = 0;
return webm::Status{stop ? webm::Status::kOkPartial : webm::Status::kOkCompleted};
}
};
struct FrameParser final : webm::Callback {
uint64_t videoTrack, audioTrack;
uint64_t currentClusterTimecode;
uint64_t untihl;
bool skip = true;
vpx_image *lastImg = nullptr;
CHiImage *output = nullptr;
vpx_codec_ctx_t *codec;
vpx_codec_iter_t *iter;
uint64_t currentlyAt = 0;
webm::Status OnClusterBegin(const webm::ElementMetadata &metadata, const webm::Cluster &cluster, webm::Action *action) final override {
currentClusterTimecode = cluster.timecode.value();
return webm::Status(webm::Status::kOkCompleted);
}
webm::Status OnBlockBegin(const webm::ElementMetadata &metadata, const webm::Block &block, webm::Action *action) final override {
/*if(block.track_number == videoTrack) {
printf("%lu %lu %i\n", currentClusterTimecode + block.timecode, untihl, currentlyAt <= untihl && currentClusterTimecode + block.timecode >= untihl);
}*/
if(block.track_number != videoTrack) {
*action = webm::Action::kSkip;
} else {
if(currentlyAt <= untihl && currentClusterTimecode + block.timecode >= untihl) {
skip = false;
} else {
skip = true;
}
currentlyAt = currentClusterTimecode + block.timecode;
}
return webm::Status(webm::Status::kOkCompleted);
}
webm::Status OnSimpleBlockBegin(const webm::ElementMetadata &metadata, const webm::SimpleBlock &block, webm::Action *action) final override {
return OnBlockBegin(metadata, block, action);
}
webm::Status OnFrame(const webm::FrameMetadata &metadata, webm::Reader *reader, uint64_t *bytes_remaining) final override {
//printf("FRAME WITH SKIP %i\n", skip);
uint8_t *data = new uint8_t[metadata.size];
uint64_t actuallyRead;
reader->Read(metadata.size, data, &actuallyRead);
vpx_codec_decode(codec, data, metadata.size, NULL, 0);
vpx_image *img = NULL;
while((img = vpx_codec_get_frame(codec, iter)) != NULL) {
if(lastImg) vpx_img_free(lastImg);
lastImg = img;
}
if(!skip && lastImg) {
assert(lastImg->fmt & VPX_IMG_FMT_PLANAR);
output = CHi_Image_New(2, 4, 8 * ((lastImg->d_w + 15) & ~15), lastImg->d_w, lastImg->d_h, NULL);
__m128i z = _mm_set1_epi32(0);
__m128i alpha = _mm_set_epi32(0xFFFF0000, 0, 0xFFFF0000, 0);
__m128i sub16 = _mm_set1_epi32(-16);
__m128i sub128 = _mm_set1_epi32(-128);
#pragma omp parallel for simd
for(size_t y = 0; y < lastImg->d_h; y++) {
for(size_t x = 0; x < lastImg->d_w; x += 4) {
__m128i ychannel = _mm_loadu_si128((__m128i*) (lastImg->planes[VPX_PLANE_Y] + y * lastImg->stride[VPX_PLANE_Y] + x));
__m128i uchannel = _mm_loadu_si128((__m128i*) (lastImg->planes[VPX_PLANE_U] + y / 2 * lastImg->stride[VPX_PLANE_U] + x / 2));
uchannel = _mm_unpacklo_epi8(uchannel, uchannel); // stretch color channels
__m128i vchannel = _mm_loadu_si128((__m128i*) (lastImg->planes[VPX_PLANE_V] + y / 2 * lastImg->stride[VPX_PLANE_V] + x / 2));
vchannel = _mm_unpacklo_epi8(vchannel, vchannel); // stretch color channels
/* Interleave with zeroes to push out 12 of 16 pixels (we're working in groups of four) */
__m128i ylo = _mm_add_epi32(sub16, _mm_unpacklo_epi16(_mm_unpacklo_epi8(ychannel, z), z));
__m128i ulo = _mm_add_epi32(sub128, _mm_unpacklo_epi16(_mm_unpacklo_epi8(uchannel, z), z));
__m128i vlo = _mm_add_epi32(sub128, _mm_unpacklo_epi16(_mm_unpacklo_epi8(vchannel, z), z));
/* Start parallel matrix multiplication (BT.709 matrix * 255/219 to turn from studio to full range) */
/*
/ 1.164 0 1.833 \
RGB = | 1.164 -0.218 -0.545 | * (Y - 16, U - 128, V - 128)
\ 1.164 2.160 0 /
*/
__m128i partY = _mm_mullo_epi32(ylo, _mm_set1_epi32(297));
__m128i partVR = _mm_mullo_epi32(vlo, _mm_set1_epi32(467));
__m128i partUG = _mm_mullo_epi32(ulo, _mm_set1_epi32(-56));
__m128i partVG = _mm_mullo_epi32(vlo, _mm_set1_epi32(-139));
__m128i partUB = _mm_mullo_epi32(ulo, _mm_set1_epi32(551));
/* Finish matrix multiplication by summing up parts (finishing the dot products), clip */
__m128i r = _mm_max_epi32(z, _mm_min_epi32(_mm_set1_epi32(0xFFFF), _mm_add_epi32(partY, partVR)));
__m128i g = _mm_max_epi32(z, _mm_min_epi32(_mm_set1_epi32(0xFFFF), _mm_add_epi32(partY, _mm_add_epi32(partUG, partVG))));
__m128i b = _mm_max_epi32(z, _mm_min_epi32(_mm_set1_epi32(0xFFFF), _mm_add_epi32(partY, partUB)));
r = apply_gamma_epi32(r, _mm_set1_ps(2.2f));
g = apply_gamma_epi32(g, _mm_set1_ps(2.2f));
b = apply_gamma_epi32(b, _mm_set1_ps(2.2f));
__m128i rgblo = _mm_or_si128(alpha, _mm_or_si128(_mm_or_si128(_mm_unpacklo_epi32(b, z), _mm_slli_si128(_mm_unpacklo_epi32(g, z), 2)), _mm_slli_si128(_mm_unpacklo_epi32(r, z), 4)));
_mm_stream_si128((__m128i*) ((uintptr_t) output->data16 + y * output->stride + x * 8 + 0), rgblo);
__m128i rgbhi = _mm_or_si128(alpha, _mm_or_si128(_mm_or_si128(_mm_unpackhi_epi32(b, z), _mm_slli_si128(_mm_unpackhi_epi32(g, z), 2)), _mm_slli_si128(_mm_unpackhi_epi32(r, z), 4)));
_mm_stream_si128((__m128i*) ((uintptr_t) output->data16 + y * output->stride + x * 8 + 16), rgbhi);
}
}
}
delete[] data;
*bytes_remaining = 0;
webm::Status ret{skip ? webm::Status::kOkCompleted : webm::Status::kOkPartial};
skip = true;
return ret;
}
};
struct CHiMovieNode {
int64_t timeCache = -1;
char *filepathCache;
FILE *vf;
webm::FileReader vreader;
webm::WebmParser vparser;
FrameParser fp;
std::string vcodecid;
size_t vw, vh;
FILE *af;
webm::FileReader areader;
webm::WebmParser aparser;
AudioParser ap;
std::vector<webm::CuePoint> cuepoints;
uint64_t segmentOff, videoTrack, audioTrack;
double duration;
vpx_codec_ctx_t codec;
vpx_codec_iter_t iter;
CHiPubNode pub;
};
webm::Status CueParser::OnInfo(const webm::ElementMetadata &metadata, const webm::Info &info) {
node->duration = info.duration.value() / 1000;
return webm::Status(webm::Status::kOkCompleted);
}
webm::Status CueParser::OnTrackEntry(const webm::ElementMetadata &metadata, const webm::TrackEntry &info) {
if(info.track_type.value() == webm::TrackType::kVideo && info.is_enabled.value() /*&& !info.uses_lacing.value()*/) {
node->vcodecid = info.codec_id.value();
node->videoTrack = info.track_number.value();
node->vw = info.video.value().pixel_width.value();
node->vh = info.video.value().pixel_height.value();
}
if(info.track_type.value() == webm::TrackType::kAudio && info.is_enabled.value()) {
node->audioTrack = info.track_number.value();
}
return webm::Status(webm::Status::kOkCompleted);
}
webm::Status CueParser::OnSegmentBegin(const webm::ElementMetadata &metadata, webm::Action *action) {
node->segmentOff = metadata.position + metadata.header_size;
return webm::Status(webm::Status::kOkCompleted);
}
webm::Status CueParser::OnCuePoint(const webm::ElementMetadata &metadata, const webm::CuePoint &cue) {
node->cuepoints.push_back(cue);
return webm::Status(webm::Status::kOkCompleted);
}
static int movie_perform(CHiPubNode *pub) {
CHiMovieNode *node = (CHiMovieNode*) ((uintptr_t) pub - offsetof(CHiMovieNode, pub));
int64_t t;
if(pub->sinks[1].type == CUTIHI_VAL_NONE) t = CHi_Time_Get(pub->ng) * 1000;
else t = CHi_Crawl(&pub->sinks[1])->data.vec4[0] * 1000;
pub->sources[0].type = CUTIHI_VAL_SAMPLE;
char *filepath = CHi_Crawl(&pub->sinks[0])->data.text;
if(!node->filepathCache || strcmp(node->filepathCache, filepath) != 0) {
node->vf = fopen(filepath, "rb");
new (&node->vreader) webm::FileReader{node->vf};
new (&node->vparser) webm::WebmParser{};
node->af = fopen(filepath, "rb");
new (&node->areader) webm::FileReader{node->af};
new (&node->aparser) webm::WebmParser{};
node->cuepoints.clear();
CueParser cp{node};
node->vparser.Feed(&cp, &node->vreader);
free(node->filepathCache);
node->filepathCache = filepath;
node->timeCache = std::numeric_limits<int64_t>::max();
if(node->vcodecid == "V_VP9") {
vpx_codec_dec_init(&node->codec, vpx_codec_vp9_dx(), NULL, 0);
} else if(node->vcodecid == "V_VP8") {
vpx_codec_dec_init(&node->codec, vpx_codec_vp8_dx(), NULL, 0);
} else {
return 1;
}
new (&node->fp) FrameParser{};
node->fp.videoTrack = node->videoTrack;
node->fp.codec = &node->codec;
node->fp.iter = &node->iter;
new (&node->ap) AudioParser{};
int error;
node->ap.opus = opus_decoder_create(48000, 1, &error);
node->ap.audioTrack = node->audioTrack;
}
if(t == node->timeCache) {
return 1;
}
if(pub->sources[0].data.sample) {
CHi_Image_Free(pub->sources[0].data.sample);
pub->sources[0].data.sample = nullptr;
}
if(t >= 0 && t < 1000 * node->duration) {
if(t < node->timeCache || (t - node->timeCache) > 5000) {
if(node->cuepoints.size() > 0) {
size_t i;
for(i = 0; i < node->cuepoints.size(); i++) {
if(t < node->cuepoints[i].time.value()) {
break;
}
}
if(i != 0) i--;
for(webm::Element<webm::CueTrackPositions> &p : node->cuepoints[i].cue_track_positions) {
if(p.value().track.value() == node->videoTrack) {
fseek(node->vf, node->segmentOff + p.value().cluster_position.value(), SEEK_SET);
fseek(node->af, node->segmentOff + p.value().cluster_position.value(), SEEK_SET);
break;
}
}
} else {
fseek(node->vf, 0, SEEK_SET);
fseek(node->af, 0, SEEK_SET);
}
}
node->fp.untihl = t;
node->ap.untihl = t;
/* Always necessary for some reason, else stops parsing after seek (as in no callbacks called).. */
node->vparser.DidSeek();
node->aparser.DidSeek();
node->vparser.Feed(&node->fp, &node->vreader);
node->aparser.Feed(&node->ap, &node->areader);
pub->sources[0].data.sample = node->fp.output;
node->timeCache = t;
}
if(!pub->sources[0].data.sample) {
pub->sources[0].data.sample = CHi_Image_New(2, 4, 8 * node->vw, node->vw, node->vh, NULL);
}
size_t width = roundf(CHi_Time_GetDelta(pub->ng) * 48000);
CHiImage *aud = CHi_Image_New(4, 1, 4 * width, width, 1, NULL);
if(node->pub.ng->compilationStatus == CUTIHI_COMP_RUNNING) {
if(node->ap.sampleReadI + width > SAMPLE_ARR) {
memcpy(aud->data16, node->ap.sampleArray + node->ap.sampleReadI, sizeof(*node->ap.sampleArray) * (SAMPLE_ARR - node->ap.sampleReadI));
memcpy(aud->data16 + SAMPLE_ARR - node->ap.sampleReadI, node->ap.sampleArray, sizeof(*node->ap.sampleArray) * (width - SAMPLE_ARR + node->ap.sampleReadI));
} else {
memcpy(aud->data16, node->ap.sampleArray + node->ap.sampleReadI, sizeof(*node->ap.sampleArray) * width);
}
node->ap.sampleReadI = (node->ap.sampleReadI + width) % SAMPLE_ARR;
} else {
memset(aud->data16, 0, aud->stride * aud->height);
}
if(pub->sources[1].data.sample) CHi_Image_Free(pub->sources[1].data.sample);
pub->sources[1].type = CUTIHI_VAL_SAMPLE;
pub->sources[1].data.sample = aud;
pub->clean = 0;
return 1;
}
extern "C" {
CUTIVIS CHiPubNode *CHi_Movie() {
CHiMovieNode *n = (CHiMovieNode*) malloc(sizeof(*n));
new (n) CHiMovieNode();
n->pub.type = CUTIHI_T('CMov','ie ');
n->pub.Perform = movie_perform;
n->pub.clean = 0;
n->pub.sinkCount = 2;
n->pub.sinks = (CHiValue*) calloc(sizeof(*n->pub.sinks), n->pub.sinkCount);
n->pub.sinks[1].type = CUTIHI_VAL_VEC4;
n->pub.sinks[1].data.vec4[0] = 0;
n->pub.sourceCount = 2;
n->pub.sources = (CHiValue*) calloc(sizeof(*n->pub.sources), n->pub.sourceCount);
return &n->pub;
}
}

431
hi/webmenc.cpp Normal file
View File

@@ -0,0 +1,431 @@
#include"node.h"
#include<vpx/vpx_encoder.h>
#include<vpx/vp8cx.h>
#include<new>
#include<mkvmuxer/mkvwriter.h>
#include<assert.h>
#include"mode.h"
#include"img.h"
#include<math.h>
#include<smmintrin.h>
#include<queue>
#include<string.h>
#include"linearity.h"
struct CHiEncodeVP9Node {
vpx_codec_ctx_t codec;
vpx_codec_enc_cfg_t cfg;
enum {
WAITING, IN_PROGRESS
} state;
uint8_t *outY, *outU, *outV;
uint16_t strideY, strideU, strideV;
vpx_codec_iface_t *iface;
CHiPubNode pub;
};
static int encodevp9_perform(CHiPubNode *pub) {
CHiEncodeVP9Node *node = (CHiEncodeVP9Node*) ((uintptr_t) pub - offsetof(CHiEncodeVP9Node, pub));
pub->sources[0].type = CUTIHI_VAL_VP9BS;
pub->sources[0].data.bitstream = NULL;
if(node->state == CHiEncodeVP9Node::WAITING) return 1;
CHiImage *rgbIn = (CHiImage*) CHi_Crawl(&pub->sinks[0])->data.sample;
#pragma omp parallel for simd
for(size_t y = 0; y < node->cfg.g_h; y += 2) {
for(size_t x = 0; x < node->cfg.g_w; x += 16) {
__m128i rgb, partY, partU, partV, dotY, dotU, dotV;
__m128i wipY0 = _mm_setzero_si128();
__m128i wipY1 = _mm_setzero_si128();
__m128i wipU = _mm_setzero_si128();
__m128i wipV = _mm_setzero_si128();
__m128i tempU = _mm_setzero_si128();
__m128i tempV = _mm_setzero_si128();
#define DO_DAH_DOO_DOO(LoOrHi, shufY, shufUV) \
/* Process top two */\
rgb = _mm_srli_epi16(apply_gamma_epi16(line0, _mm_set1_ps(1 / 2.2f)), 8); \
/* Start matrix multiplication (BT.709 + full->studio range) */\
partY = _mm_mullo_epi16(rgb, _mm_set_epi16(0, 47, 157, 16, 0, 47, 157, 16));\
partU = _mm_mullo_epi16(rgb, _mm_set_epi16(0, -25, -85, 110, 0, -25, -85, 110));\
partV = _mm_mullo_epi16(rgb, _mm_set_epi16(0, 110, -100, -10, 0, 110, -100, -10));\
/* Finish mat-mul with dot products */\
dotY = _mm_madd_epi16(partY, _mm_set1_epi16(1));\
dotY = _mm_hadd_epi32(dotY, _mm_setzero_si128());\
dotU = _mm_madd_epi16(partU, _mm_set1_epi16(1));\
dotU = _mm_hadd_epi32(dotU, _mm_setzero_si128());\
dotV = _mm_madd_epi16(partV, _mm_set1_epi16(1));\
dotV = _mm_hadd_epi32(dotV, _mm_setzero_si128());\
/* Insert Ys */\
wipY0 = _mm_or_si128(wipY0, _mm_shuffle_epi8(dotY, shufY));\
/* Save top UV */\
tempU = dotU;\
tempV = dotV;\
\
/* Process bottom two */\
rgb = _mm_srli_epi16(apply_gamma_epi16(line1, _mm_set1_ps(1 / 2.2f)), 8); \
/* Start matrix multiplication (BT.709 + full->studio range) */\
partY = _mm_mullo_epi16(rgb, _mm_set_epi16(0, 47, 157, 16, 0, 47, 157, 16));\
partU = _mm_mullo_epi16(rgb, _mm_set_epi16(0, -25, -85, 110, 0, -25, -85, 110));\
partV = _mm_mullo_epi16(rgb, _mm_set_epi16(0, 110, -100, -10, 0, 110, -100, -10));\
/* Finish mat-mul with dot products */\
dotY = _mm_madd_epi16(partY, _mm_set1_epi16(1));\
dotY = _mm_hadd_epi32(dotY, _mm_setzero_si128());\
dotU = _mm_madd_epi16(partU, _mm_set1_epi16(1));\
dotU = _mm_hadd_epi32(dotU, _mm_setzero_si128());\
dotV = _mm_madd_epi16(partV, _mm_set1_epi16(1));\
dotV = _mm_hadd_epi32(dotV, _mm_setzero_si128());\
/* Insert Ys */\
wipY1 = _mm_or_si128(wipY1, _mm_shuffle_epi8(dotY, shufY));\
/* Save bottom UVs */\
tempU = _mm_hadd_epi32(_mm_add_epi32(tempU, dotU), _mm_setzero_si128());\
tempV = _mm_hadd_epi32(_mm_add_epi32(tempV, dotV), _mm_setzero_si128());\
\
/* Insert UVs */\
wipU = _mm_or_si128(wipU, _mm_shuffle_epi8(_mm_srli_epi32(tempU, 2), shufUV));\
wipV = _mm_or_si128(wipV, _mm_shuffle_epi8(_mm_srli_epi32(tempV, 2), shufUV));
__m128i line0 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 0) * rgbIn->stride + (x + 0) * 8)); // Load two pixels
__m128i line1 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 1) * rgbIn->stride + (x + 0) * 8)); // Load two pixels
DO_DAH_DOO_DOO(_mm_unpacklo_epi8,
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 5, 1),
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 1));
line0 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 0) * rgbIn->stride + (x + 2) * 8)); // Load two pixels
line1 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 1) * rgbIn->stride + (x + 2) * 8)); // Load two pixels
DO_DAH_DOO_DOO(_mm_unpacklo_epi8,
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 5, 1, -128, -128),
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 1, -128));
line0 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 0) * rgbIn->stride + (x + 4) * 8)); // Load two pixels
line1 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 1) * rgbIn->stride + (x + 4) * 8)); // Load two pixels
DO_DAH_DOO_DOO(_mm_unpacklo_epi8,
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 5, 1, -128, -128, -128, -128),
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 1, -128, -128));
line0 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 0) * rgbIn->stride + (x + 6) * 8)); // Load two pixels
line1 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 1) * rgbIn->stride + (x + 6) * 8)); // Load two pixels
DO_DAH_DOO_DOO(_mm_unpacklo_epi8,
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, 5, 1, -128, -128, -128, -128, -128, -128),
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 1, -128, -128, -128));
line0 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 0) * rgbIn->stride + (x + 8) * 8)); // Load two pixels
line1 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 1) * rgbIn->stride + (x + 8) * 8)); // Load two pixels
DO_DAH_DOO_DOO(_mm_unpacklo_epi8,
_mm_set_epi8(-128, -128, -128, -128, -128, -128, 5, 1, -128, -128, -128, -128, -128, -128, -128, -128),
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 1, -128, -128, -128, -128));
line0 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 0) * rgbIn->stride + (x + 10) * 8)); // Load two pixels
line1 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 1) * rgbIn->stride + (x + 10) * 8)); // Load two pixels
DO_DAH_DOO_DOO(_mm_unpacklo_epi8,
_mm_set_epi8(-128, -128, -128, -128, 5, 1, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128),
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 1, -128, -128, -128, -128, -128));
line0 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 0) * rgbIn->stride + (x + 12) * 8)); // Load two pixels
line1 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 1) * rgbIn->stride + (x + 12) * 8)); // Load two pixels
DO_DAH_DOO_DOO(_mm_unpacklo_epi8,
_mm_set_epi8(-128, -128, 5, 1, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128),
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, 1, -128, -128, -128, -128, -128, -128));
line0 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 0) * rgbIn->stride + (x + 14) * 8)); // Load two pixels
line1 = _mm_load_si128((__m128i*) ((uintptr_t) rgbIn->data16 + (y + 1) * rgbIn->stride + (x + 14) * 8)); // Load two pixels
DO_DAH_DOO_DOO(_mm_unpacklo_epi8,
_mm_set_epi8( 5, 1, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128),
_mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, 1, -128, -128, -128, -128, -128, -128, -128));
_mm_stream_si128((__m128i*) &node->outY[node->strideY * (y + 0) + x], _mm_add_epi8(_mm_set1_epi8(16), wipY0));
_mm_stream_si128((__m128i*) &node->outY[node->strideY * (y + 1) + x], _mm_add_epi8(_mm_set1_epi8(16), wipY1));
_mm_storeu_si128((__m128i*) &node->outU[node->strideU * (y / 2) + x / 2], _mm_add_epi8(wipU, _mm_set1_epi8(128)));
_mm_storeu_si128((__m128i*) &node->outV[node->strideV * (y / 2) + x / 2], _mm_add_epi8(wipV, _mm_set1_epi8(128)));
}
}
vpx_image_t vpxraw;
vpxraw.fmt = VPX_IMG_FMT_I420;
vpxraw.cs = VPX_CS_BT_709;
vpxraw.range = VPX_CR_STUDIO_RANGE;
vpxraw.bit_depth = 8;
vpxraw.w = vpxraw.d_w = node->cfg.g_w;
vpxraw.h = vpxraw.d_h = node->cfg.g_h;
vpxraw.r_w = vpxraw.r_h = 0;
vpxraw.x_chroma_shift = vpxraw.y_chroma_shift = 1;
vpxraw.img_data_owner = 0;
vpxraw.self_allocd = 0;
vpxraw.bps = 12;
vpxraw.stride[VPX_PLANE_Y] = node->strideY;
vpxraw.planes[VPX_PLANE_Y] = node->outY;
vpxraw.stride[VPX_PLANE_U] = node->strideU;
vpxraw.planes[VPX_PLANE_U] = node->outU;
vpxraw.stride[VPX_PLANE_V] = node->strideV;
vpxraw.planes[VPX_PLANE_V] = node->outV;
vpx_codec_encode(&node->codec, &vpxraw, CHi_Time_Get(pub->ng) * 1000.f, 1, 0, VPX_DL_REALTIME);
auto ret = (CHiBSFrames*) malloc(sizeof(CHiBSFrames));
ret->count = 0;
vpx_codec_iter_t iter = NULL;
const vpx_codec_cx_pkt_t *pkt;
while((pkt = vpx_codec_get_cx_data(&node->codec, &iter)) != NULL) {
if(pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
ret = (CHiBSFrames*) realloc(ret, sizeof(CHiBSFrames) + sizeof(CHiBSFrame) * (ret->count + 1));
ret->data[ret->count].timestamp = pkt->data.frame.pts;
ret->data[ret->count].sz = pkt->data.frame.sz;
ret->data[ret->count].flags = pkt->data.frame.flags & VPX_FRAME_IS_KEY;
ret->data[ret->count].ptr = malloc(ret->data[ret->count].sz);
memcpy(ret->data[ret->count].ptr, pkt->data.frame.buf, ret->data[ret->count].sz);
ret->count++;
}
}
// if(pktRet) v->queueOut.enqueue(pktRet);
//memcpy(node->vpxraw.planes[VPX_PLANE_Y], VIPS_IMAGE_ADDR(y, 0, 0), node->vpxraw.stride[VPX_PLANE_Y] * node->vpxraw.d_h);
//memcpy(node->vpxraw.planes[VPX_PLANE_U], VIPS_IMAGE_ADDR(u, 0, 0), node->vpxraw.stride[VPX_PLANE_U] * (node->vpxraw.d_h >> node->vpxraw.y_chroma_shift));
//memcpy(node->vpxraw.planes[VPX_PLANE_V], VIPS_IMAGE_ADDR(v, 0, 0), node->vpxraw.stride[VPX_PLANE_V] * (node->vpxraw.d_h >> node->vpxraw.y_chroma_shift));
//const vpx_codec_cx_pkt_t *pkt;
//while(!node->queueOut.try_dequeue(pkt)) usleep(0);
pub->sources[0].data.bitstream = ret;
return 1;
}
CUTIVIS CHiPubNode *CHi_EncodeVP8() {
CHiEncodeVP9Node *n = (CHiEncodeVP9Node*) malloc(sizeof(*n));
new (n) CHiEncodeVP9Node();
n->pub.type = CUTIHI_T('CEnc','GVP8');
n->pub.Start = CHi_EncodeVP9_Start;
n->pub.Perform = encodevp9_perform;
n->pub.Stop = CHi_EncodeVP9_Stop;
n->pub.clean = 0;
n->pub.sinks = (CHiValue*) calloc(sizeof(*n->pub.sinks), n->pub.sinkCount = 1);
n->pub.sources = (CHiValue*) calloc(sizeof(*n->pub.sources), n->pub.sourceCount = 1);
n->state = CHiEncodeVP9Node::WAITING;
n->iface = vpx_codec_vp8_cx();
return &n->pub;
}
CUTIVIS CHiPubNode *CHi_EncodeVP9() {
CHiEncodeVP9Node *n = (CHiEncodeVP9Node*) malloc(sizeof(*n));
new (n) CHiEncodeVP9Node();
n->pub.type = CUTIHI_T('CEnc','GVP9');
n->pub.Start = CHi_EncodeVP9_Start;
n->pub.Perform = encodevp9_perform;
n->pub.Stop = CHi_EncodeVP9_Stop;
n->pub.clean = 0;
n->pub.sinks = (CHiValue*) calloc(sizeof(*n->pub.sinks), n->pub.sinkCount = 1);
n->pub.sources = (CHiValue*) calloc(sizeof(*n->pub.sources), n->pub.sourceCount = 1);
n->state = CHiEncodeVP9Node::WAITING;
n->iface = vpx_codec_vp9_cx();
return &n->pub;
}
CUTIVIS int CHi_EncodeVP9_Start(CHiPubNode *pubn) {
CHiEncodeVP9Node *node = (CHiEncodeVP9Node*) ((uintptr_t) pubn - offsetof(CHiEncodeVP9Node, pub));
node->state = CHiEncodeVP9Node::IN_PROGRESS;
CHiImage *firstFrame = (CHiImage*) CHi_Crawl(&pubn->sinks[0])->data.sample;
vpx_codec_enc_config_default(node->iface, &node->cfg, 0);
node->cfg.g_w = firstFrame->width;
node->cfg.g_h = firstFrame->height;
node->cfg.g_timebase.num = 1;
node->cfg.g_timebase.den = 30;
node->cfg.g_lag_in_frames = 0;
node->cfg.g_threads = 8;
node->cfg.kf_mode = VPX_KF_AUTO;
node->cfg.kf_max_dist = 300;
node->cfg.rc_end_usage = VPX_VBR;
node->cfg.rc_target_bitrate = 512;
node->cfg.rc_min_quantizer = 4;
node->cfg.rc_max_quantizer = 48;
vpx_codec_enc_init(&node->codec, node->iface, &node->cfg, 0);
vpx_codec_control(&node->codec, VP8E_SET_CPUUSED, 8);
vpx_codec_control(&node->codec, VP9E_SET_ROW_MT, 1);
vpx_codec_control(&node->codec, VP9E_SET_TILE_COLUMNS, 2);
vpx_codec_control(&node->codec, VP9E_SET_TILE_ROWS, 1);
vpx_codec_control(&node->codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN);
node->strideY = (node->cfg.g_w + 64) & ~63;
node->strideU = (node->cfg.g_w / 2 + 64) & ~63;
node->strideV = (node->cfg.g_w / 2 + 64) & ~63;
node->outY = (uint8_t*) _mm_malloc(node->strideY * node->cfg.g_h, 16);
node->outU = (uint8_t*) _mm_malloc(node->strideU * node->cfg.g_h / 2, 16);
node->outV = (uint8_t*) _mm_malloc(node->strideV * node->cfg.g_h / 2, 16);
return 1;
}
CUTIVIS int CHi_EncodeVP9_Stop(CHiPubNode *pubn) {
CHiEncodeVP9Node *node = (CHiEncodeVP9Node*) ((uintptr_t) pubn - offsetof(CHiEncodeVP9Node, pub));
node->state = CHiEncodeVP9Node::WAITING;
_mm_free(node->outY);
_mm_free(node->outU);
_mm_free(node->outV);
return 1;
}
struct CHiMuxWebmNode {
CHiPubNode pub;
mkvmuxer::MkvWriter w;
mkvmuxer::Segment seg;
size_t videoTrack, audioTrack;
std::queue<CHiBSFrame> audioBacklog;
std::queue<CHiBSFrame> videoBacklog;
};
static int muxwebm_perform(CHiPubNode *pubn) {
using namespace mkvmuxer;
CHiMuxWebmNode *alln = (CHiMuxWebmNode*) pubn;
if(pubn->sinks[1].data.linked.to) {
CHiBSFrames *opus = CHi_Crawl(&pubn->sinks[1])->data.bitstream;
for(size_t i = 0; i < opus->count; i++) {
alln->audioBacklog.push(opus->data[i]);
}
}
auto vp9bs = CHi_Crawl(&pubn->sinks[0])->data.bitstream;
if(vp9bs) {
for(size_t i = 0; i < vp9bs->count; i++) {
alln->videoBacklog.push(vp9bs->data[i]);
}
}
while(pubn->sinks[1].data.linked.to && alln->audioBacklog.size() > 0 && alln->videoBacklog.size() > 0 && alln->audioBacklog.front().timestamp <= alln->videoBacklog.front().timestamp) {
Frame frame;
frame.Init((const uint8_t*) alln->audioBacklog.front().ptr, alln->audioBacklog.front().sz);
frame.set_track_number(alln->audioTrack);
frame.set_timestamp(alln->audioBacklog.front().timestamp * 1000000L);
frame.set_is_key(true);
alln->seg.AddGenericFrame(&frame);
alln->audioBacklog.pop();
}
if(pubn->sinks[1].data.linked.to == NULL || (alln->audioBacklog.size() > 0 && alln->videoBacklog.size() > 0 && alln->audioBacklog.front().timestamp >= alln->videoBacklog.front().timestamp)) {
Frame frame;
if(!frame.Init((const uint8_t*) alln->videoBacklog.front().ptr, alln->videoBacklog.front().sz)) puts("INIT FAIL");
frame.set_track_number(alln->videoTrack);
frame.set_timestamp(alln->videoBacklog.front().timestamp * 1000000L);
frame.set_is_key(!!(alln->videoBacklog.front().flags & CUTIHI_BS_FLAG_KEY));
if(!alln->seg.AddGenericFrame(&frame)) puts("ADD FAIL");
alln->videoBacklog.pop();
}
return 1;
}
CUTIVIS CHiPubNode *CHi_MuxWebm() {
CHiMuxWebmNode *n = (CHiMuxWebmNode*) malloc(sizeof(*n));
n->pub.type = CUTIHI_T('CExp','Webm');
n->pub.Start = CHi_MuxWebm_Start;
n->pub.Perform = muxwebm_perform;
n->pub.Stop = CHi_MuxWebm_Stop;
n->pub.clean = 0;
n->pub.sinks = (CHiValue*) calloc(sizeof(*n->pub.sinks), n->pub.sinkCount = 3);
n->pub.sourceCount = 0;
n->pub.sources = NULL;
new (&n->audioBacklog) std::queue<CHiBSFrame>();
new (&n->videoBacklog) std::queue<CHiBSFrame>();
return &n->pub;
}
CUTIVIS int CHi_MuxWebm_Start(CHiPubNode *pubn) {
using namespace mkvmuxer;
CHiMuxWebmNode *alln = (CHiMuxWebmNode*) pubn;
new (&alln->w) MkvWriter{};
alln->w.Open(CHi_Crawl(&pubn->sinks[CUTIHI_MUXWEBM_IN_FILENAME])->data.text);
new (&alln->seg) Segment{};
alln->seg.Init(&alln->w);
alln->seg.AccurateClusterDuration(true);
alln->seg.UseFixedSizeClusterTimecode(false);
alln->seg.set_mode(Segment::kFile);
alln->seg.OutputCues(true);
alln->seg.set_duration(pubn->ng->duration * 1000);
alln->seg.GetSegmentInfo()->set_timecode_scale(1000000);
alln->seg.GetSegmentInfo()->set_writing_app("Cuticle");
/* Hack into first frame to get resolution */
CHiPubNode *evp9 = pubn->sinks[0].data.linked.to;
CHiImage *firstFrame = (CHiImage*) CHi_Crawl(&evp9->sinks[0])->data.sample;
alln->videoTrack = alln->seg.AddVideoTrack(firstFrame->width, firstFrame->height, 0);
VideoTrack *track = (VideoTrack*) alln->seg.GetTrackByNumber(alln->videoTrack);
track->set_codec_id(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_VP9BS ? Tracks::kVp9CodecId : Tracks::kVp8CodecId);
track->set_frame_rate(30);
Colour colourspace;
colourspace.set_matrix_coefficients(Colour::MatrixCoefficients::kBt709);
colourspace.set_range(Colour::Range::kBroadcastRange);
colourspace.set_transfer_characteristics(Colour::TransferCharacteristics::kIturBt709Tc);
colourspace.set_primaries(Colour::Primaries::kIturBt709P);
track->SetColour(colourspace);
alln->seg.CuesTrack(alln->videoTrack);
if(pubn->sinks[1].data.linked.to) {
struct __attribute__((packed)) {
uint32_t magic1;
uint32_t magic2;
uint8_t version;
uint8_t channels; // NUMBER OF CHANNELS IS HARDCODED TO ONE
uint16_t preskip;
uint32_t sampleRate;
uint16_t gain;
uint8_t family;
} header = {0x7375704f, 0x64616548, 1, 1, 3840, 48000, 0, 0};
alln->audioTrack = alln->seg.AddAudioTrack(48000, 1, 0);
AudioTrack *atrack = (AudioTrack*) alln->seg.GetTrackByNumber(alln->audioTrack);
atrack->set_codec_id(Tracks::kOpusCodecId);
atrack->set_seek_pre_roll(80000000);
atrack->SetCodecPrivate((const uint8_t*) &header, sizeof(header));
}
return 1;
}
CUTIVIS int CHi_MuxWebm_Stop(CHiPubNode *pubn) {
CHiMuxWebmNode *alln =(CHiMuxWebmNode*) pubn;
alln->seg.Finalize();
alln->w.Close();
return 1;
}

194
hi/window.c Normal file
View File

@@ -0,0 +1,194 @@
#include"node.h"
#include<stdlib.h>
#include<X11/Xlib.h>
#include<X11/Xutil.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<X11/extensions/XShm.h>
#include<tmmintrin.h>
#include<smmintrin.h>
#include<time.h>
#include<string.h>
#include"img.h"
#include"linearity.h"
static Display *d;
static Window root;
static int find_window(Display *d, Window *w, const char *contains) {
if(contains) {
int found = 0;
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
Atom actualType;
int format;
unsigned long numItems, bytesAfter;
Window *list;
XTextProperty windowName;
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
if(status >= Success) {
for(int i = 0; i < numItems; i++) {
status = XGetWMName(d, list[i], &windowName);
if(status >= Success) {
if(windowName.value && strstr(windowName.value, contains)) {
*w = list[i];
found = 1;
break;
}
}
}
}
XFree(list);
return found;
} else {
*w = root;
return 1;
}
}
typedef struct {
CHiPubNode pub;
Window xcache;
XImage *ximg;
XShmSegmentInfo shminfo;
CHiImage *vcache;
} CHiWindowNode;
static int window_perform(CHiPubNode *n) {
CHiWindowNode *w = (CHiWindowNode*) n;
Window toshoot;
if(!find_window(d, &toshoot, CHi_Crawl(&n->sinks[0])->data.text)) return 0;
size_t stride;
if(!w->xcache || w->xcache != toshoot) {
w->xcache = toshoot;
XWindowAttributes attrs;
XGetWindowAttributes(d, toshoot, &attrs);
w->ximg = XShmCreateImage(d, attrs.visual, 32, ZPixmap, NULL, &w->shminfo, attrs.width, attrs.height);
stride = ((w->ximg->bytes_per_line + 15) & ~15);
w->shminfo.shmid = shmget(IPC_PRIVATE, stride * w->ximg->height, IPC_CREAT | 0777);
w->shminfo.shmaddr = w->ximg->data = shmat(w->shminfo.shmid, 0, 0);
w->shminfo.readOnly = False;
XShmAttach(d, &w->shminfo);
w->vcache = CHi_Image_New(2, 4, 8 * attrs.width, attrs.width, attrs.height, NULL);
} else {
stride = ((w->ximg->bytes_per_line + 15) & ~15);
}
XWindowAttributes toshootattrs;
XGetWindowAttributes(d, w->xcache, &toshootattrs);
XShmGetImage(d, w->xcache, w->ximg, 0, 0, AllPlanes);
// Turn u8 image to u16
#pragma omp parallel for
for(size_t y = 0; y < w->vcache->height; y++) {
for(size_t x = 0; x < w->vcache->width; x += 2) {
__m128i c = _mm_loadu_si128((__m128i*) ((uintptr_t) w->ximg->data + y * w->ximg->bytes_per_line + x * 4));
c = _mm_shuffle_epi8(c, _mm_set_epi8(7, -128, 6, -128, 5, -128, 4, -128, 3, -128, 2, -128, 1, -128, 0, -128));
c = apply_gamma_epi16(c, _mm_set_ps(1, 2.2f, 2.2f, 2.2f));
_mm_storeu_si128((__m128i*) ((uintptr_t) w->vcache->data16 + y * w->vcache->stride + x * 8), c);
}
}
n->sources[0].type = CUTIHI_VAL_SAMPLE;
n->sources[0].data.sample = w->vcache;
n->clean = 0;
return 1;
}
CUTIVIS CHiPubNode *CHi_Window() {
if(!d) {
d = XOpenDisplay(NULL);
root = RootWindow(d, DefaultScreen(d));
}
CHiWindowNode *n = malloc(sizeof(*n));
n->pub.type = CUTIHI_T('CWin','dow ');
n->pub.Start = n->pub.Stop = NULL;
n->pub.Perform = window_perform;
n->pub.clean = 0;
n->pub.sinkCount = 1;
n->pub.sinks = calloc(sizeof(*n->pub.sinks), 1);
n->pub.sourceCount = 1;
n->pub.sources = calloc(sizeof(*n->pub.sources), 1);
n->xcache = 0;
n->vcache = NULL;
return &n->pub;
}
// All of the following are ews
CUTIVIS size_t CHi_Window_GetSourceCount() {
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
Atom actualType;
int format;
unsigned long numItems, bytesAfter;
Window *list;
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
return status >= Success ? numItems : 0;
}
CUTIVIS const char *CHi_Window_GetSourceName(size_t idx) {
int found = 0;
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
Atom actualType;
int format;
unsigned long numItems, bytesAfter;
Window *list;
XTextProperty windowName;
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
if(status >= Success) {
status = XGetWMName(d, list[idx], &windowName);
if(status >= Success) {
found = 1;
}
}
return found ? strdup(windowName.value ? windowName.value : "") : NULL;
}
CUTIVIS uintptr_t CHi_Window_GetSourceData(size_t idx) {
Atom atom = XInternAtom(d, "_NET_CLIENT_LIST", 1);
Atom actualType;
int format;
unsigned long numItems, bytesAfter;
Window *list;
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
if(status >= Success) {
return list[idx];
}
return 0;
}
CUTIVIS size_t CHi_Window_GetNextSource(size_t i) {
return i + 1;
}