Move to Git
This commit is contained in:
		
						commit
						18da1dabcd
					
				
							
								
								
									
										24
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
 | 
			
		||||
CXXFLAGS := -D_POSIX_C_SOURCE=200809L -Wno-narrowing -march=native -flto -Wall -fvisibility=hidden -fPIC -msse4 -I./ -I/usr/local/include/sail `pkg-config --cflags pango opus libv4l2` '-Wl,-rpath,$$ORIGIN' -Wno-multichar
 | 
			
		||||
LDFLAGS := -lwebm -lpng -lvpx -lsail -lsail-manip `pkg-config --libs pango opus libv4l2` -lportaudio -lXtst
 | 
			
		||||
 | 
			
		||||
ifneq ($(RELEASE),0)
 | 
			
		||||
	CXXFLAGS := $(CXXFLAGS) -O0 -g3 -fsanitize=address
 | 
			
		||||
else
 | 
			
		||||
	CXXFLAGS := $(CXXFLAGS) -O3 -fopenmp
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
all:
 | 
			
		||||
	gcc $(CXXFLAGS) -std=c99 -shared -c -o node.o hi/node.c $(LDFLAGS)
 | 
			
		||||
	gcc $(CXXFLAGS) -std=c99 -shared -c -o window.o hi/window.c $(LDFLAGS)
 | 
			
		||||
	gcc $(CXXFLAGS) -std=c99 -shared -c -o microphone.o hi/microphone.c $(LDFLAGS)
 | 
			
		||||
	gcc $(CXXFLAGS) -std=c99 -shared -c -o mode.o hi/mode.c $(LDFLAGS)
 | 
			
		||||
	gcc $(CXXFLAGS) -std=c99 -shared -c -o img.o hi/img.c $(LDFLAGS)
 | 
			
		||||
	g++ $(CXXFLAGS) -std=c++11 -shared -c -o webmdec.o hi/webmdec.cpp $(LDFLAGS)
 | 
			
		||||
	g++ $(CXXFLAGS) -std=c++11 -shared -c -o webmenc.o hi/webmenc.cpp $(LDFLAGS)
 | 
			
		||||
	gcc $(CXXFLAGS) -std=c99 -shared -c -o opus.o hi/opus.c $(LDFLAGS)
 | 
			
		||||
	gcc $(CXXFLAGS) -std=c99 -shared -c -o webcam.o hi/webcam.c $(LDFLAGS)
 | 
			
		||||
	gcc $(CXXFLAGS) -std=c99 -shared -c -o scale.o hi/relay.c $(LDFLAGS)
 | 
			
		||||
	gcc $(CXXFLAGS) -shared -o libcutihi.so -shared node.o webmdec.o webmenc.o window.o microphone.o mode.o img.o opus.o webcam.o scale.o $(LDFLAGS)
 | 
			
		||||
	
 | 
			
		||||
	g++ $(CXXFLAGS) -std=c++11 `wx-config --cflags base,adv,core,aui` -o cuticle ui/main.cpp ui/frame.cpp ui/textctrl.cpp ui/timeline.cpp -L./ -lcutihi $(LDFLAGS) `wx-config --libs base,adv,core,aui` 
 | 
			
		||||
							
								
								
									
										26
									
								
								hi/bs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								hi/bs.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										3
									
								
								hi/defs.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define CUTIVIS __attribute__((visibility("default")))
 | 
			
		||||
							
								
								
									
										39
									
								
								hi/img.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								hi/img.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										29
									
								
								hi/img.h
									
									
									
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										114
									
								
								hi/linearity.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								hi/linearity.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										19
									
								
								hi/loopback.c
									
									
									
									
									
										Normal 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(¶ms));
 | 
			
		||||
	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
									
								
							
							
						
						
									
										209
									
								
								hi/microphone.c
									
									
									
									
									
										Normal 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, ¶ms, 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
									
								
							
							
						
						
									
										18
									
								
								hi/microphone.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										11
									
								
								hi/mode.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										19
									
								
								hi/mode.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										899
									
								
								hi/node.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										268
									
								
								hi/node.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										85
									
								
								hi/opus.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										132
									
								
								hi/relay.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										14
									
								
								hi/relay.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										43
									
								
								hi/test.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										137
									
								
								hi/webcam.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										418
									
								
								hi/webmdec.cpp
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										431
									
								
								hi/webmenc.cpp
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										194
									
								
								hi/window.c
									
									
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								keyframe.bmp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								keyframe.bmp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								keyframe_extrap.bmp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								keyframe_extrap.bmp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										838
									
								
								ui/frame.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										838
									
								
								ui/frame.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,838 @@
 | 
			
		||||
#include"frame.h"
 | 
			
		||||
 | 
			
		||||
#include<wx/menu.h>
 | 
			
		||||
#include<wx/dcclient.h>
 | 
			
		||||
#include<wx/colourdata.h>
 | 
			
		||||
#include<wx/colordlg.h>
 | 
			
		||||
#include<wx/filedlg.h>
 | 
			
		||||
#include<wx/textctrl.h>
 | 
			
		||||
#include<wx/wrapsizer.h>
 | 
			
		||||
#include<wx/stattext.h>
 | 
			
		||||
#include"timectrl.h"
 | 
			
		||||
#include<algorithm>
 | 
			
		||||
#include<hi/mode.h>
 | 
			
		||||
#include<memory>
 | 
			
		||||
#include<tmmintrin.h>
 | 
			
		||||
#include<wx/settings.h>
 | 
			
		||||
#include"hi/microphone.h"
 | 
			
		||||
#include<wx/choicdlg.h>
 | 
			
		||||
#include<wx/app.h>
 | 
			
		||||
#include<thread>
 | 
			
		||||
#include<chrono>
 | 
			
		||||
#include"timeline.h"
 | 
			
		||||
#include<functional>
 | 
			
		||||
#include<algorithm>
 | 
			
		||||
 | 
			
		||||
#define SSE_MATHFUN_WITH_CODE
 | 
			
		||||
#include<hi/kumb.h>
 | 
			
		||||
 | 
			
		||||
#include<hi/linearity.h>
 | 
			
		||||
 | 
			
		||||
Frame::Frame() : wxFrame(NULL, wxID_ANY, "Cuticle", wxDefaultPosition, {wxSystemSettings::GetMetric(wxSYS_SCREEN_X) / 2, wxSystemSettings::GetMetric(wxSYS_SCREEN_Y) / 2}) {
 | 
			
		||||
	aui.SetManagedWindow(this);
 | 
			
		||||
	
 | 
			
		||||
	viewer = new ImageViewer(this);
 | 
			
		||||
	graph = new NodeGraph(this);
 | 
			
		||||
	compsets = new CompositionSettings(this);
 | 
			
		||||
	timeline = new Timeline(this);
 | 
			
		||||
	
 | 
			
		||||
	auto info = wxAuiPaneInfo().Center().Caption("Preview").BestSize(GetSize().x, GetSize().y * 3 / 4);
 | 
			
		||||
	aui.AddPane(viewer, info);
 | 
			
		||||
	
 | 
			
		||||
	info = wxAuiPaneInfo().Bottom().Caption("Node Graph").BestSize(GetSize().x, GetSize().y * 1 / 4);
 | 
			
		||||
	aui.AddPane(graph, info);
 | 
			
		||||
	
 | 
			
		||||
	info = wxAuiPaneInfo().Right().Caption("Composition");
 | 
			
		||||
	aui.AddPane(compsets, info);
 | 
			
		||||
	
 | 
			
		||||
	info = wxAuiPaneInfo().Bottom().Row(2).Caption("Timeline");
 | 
			
		||||
	aui.AddPane(timeline, info);
 | 
			
		||||
	
 | 
			
		||||
	stba = CreateStatusBar();
 | 
			
		||||
	tlba = CreateToolBar();
 | 
			
		||||
	
 | 
			
		||||
	static wxBitmap modeImgs[] = {
 | 
			
		||||
		[CUTIHI_MODE_LIVE] = wxBitmap{"tlml.bmp", wxBITMAP_TYPE_ANY},
 | 
			
		||||
		[CUTIHI_MODE_OFFLINE] = wxBitmap{"tlmo.bmp", wxBITMAP_TYPE_ANY}
 | 
			
		||||
	};
 | 
			
		||||
	int modeTool = tlba->AddTool(wxID_ANY, "Mode", wxBitmap{"tlml.bmp", wxBITMAP_TYPE_ANY})->GetId();
 | 
			
		||||
	CHi_SetMode(CUTIHI_MODE_LIVE);
 | 
			
		||||
	tlba->Bind(wxEVT_COMMAND_TOOL_CLICKED, [=](wxCommandEvent &ev){
 | 
			
		||||
		if(ev.GetId() == modeTool) {
 | 
			
		||||
			CHi_SetMode((CHiMode) ((CHi_GetMode() + 1) % 2));
 | 
			
		||||
			tlba->SetToolNormalBitmap(modeTool, modeImgs[CHi_GetMode()]);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	tlba->Realize();
 | 
			
		||||
	
 | 
			
		||||
	aui.SetFlags(wxAUI_MGR_LIVE_RESIZE | wxAUI_MGR_DEFAULT);
 | 
			
		||||
	aui.Update();
 | 
			
		||||
	
 | 
			
		||||
	Centre();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Frame::~Frame() {
 | 
			
		||||
	aui.UnInit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GrNode::MouseOverPort(wxPoint point, bool &source, int &i) {
 | 
			
		||||
	if(point.y < 26 || point.x < 0 || point.x > GetSize().x) return false;
 | 
			
		||||
	
 | 
			
		||||
	int p = (point.y - 26) / 20;
 | 
			
		||||
	if((point.x >= 15 || p >= (int) sinks.size()) && (point.x < GetSize().x - 15 || p >= (int) sources.size())) return false;
 | 
			
		||||
	int isSource = point.x >= GetSize().x - 10;
 | 
			
		||||
	
 | 
			
		||||
	source = isSource;
 | 
			
		||||
	i = p;
 | 
			
		||||
	
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GrNode::MakeKeyframe(int sinkIdx) {
 | 
			
		||||
	auto ng = (NodeGraph*) GetParent();
 | 
			
		||||
	
 | 
			
		||||
	CHi_MakeKeyframe(ng->backendNG, this->logical, sinkIdx);
 | 
			
		||||
	
 | 
			
		||||
	((Frame*) ng->GetParent())->timeline->Refresh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80}) {
 | 
			
		||||
	Bind(wxEVT_PAINT, [this](wxPaintEvent &ev){
 | 
			
		||||
		wxPaintDC dc(this);
 | 
			
		||||
		dc.SetBrush(wxBrush{wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)});
 | 
			
		||||
		dc.SetPen(HasFocus() ? *wxRED_PEN : *wxBLACK_PEN);
 | 
			
		||||
		dc.DrawRoundedRectangle(5, 0, GetSize().x - 10, GetSize().y, 5);
 | 
			
		||||
		
 | 
			
		||||
		dc.DrawText(name, GetSize().x / 2 - dc.GetTextExtent(name).x / 2, 2);
 | 
			
		||||
		
 | 
			
		||||
		bool hoverIsSource;
 | 
			
		||||
		int hoverI;
 | 
			
		||||
		bool hover = MouseOverPort(ScreenToClient(wxGetMousePosition()), hoverIsSource, hoverI);
 | 
			
		||||
		
 | 
			
		||||
		int y = 13;
 | 
			
		||||
		int i = 0;
 | 
			
		||||
		for(Port &p : sinks) {
 | 
			
		||||
			wxColour col =	p.type == GrNode::Port::Type::FILE_OPEN ? wxColour{255, 0, 0}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::FILE_SAVE ? wxColour{255, 0, 0}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::COLOR ? wxColour{0, 0, 255}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::VEC2 ? wxColour{0, 255, 0}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::TEXT ? wxColour{255, 255, 0}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::WINDOW_SOURCE ? wxColour{255, 255, 0}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::SAMPLE ? wxColour{252, 111, 255}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::MIC_SOURCE ? wxColour{255, 255, 255}
 | 
			
		||||
						:	wxColour{128, 128, 128};
 | 
			
		||||
			
 | 
			
		||||
			if(hover && !hoverIsSource && i == hoverI) {
 | 
			
		||||
				col.Set(std::min(col.Red() + 50, 255), std::min(col.Green() + 50, 255), std::min(col.Blue() + 50, 255));
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			wxSize sz = dc.GetTextExtent(p.name);
 | 
			
		||||
			dc.SetBrush(wxBrush{col});
 | 
			
		||||
			dc.DrawText(p.name, 15, (y += 20) - sz.y / 2);
 | 
			
		||||
			dc.DrawCircle(5, y, 5);
 | 
			
		||||
			
 | 
			
		||||
			i++;
 | 
			
		||||
		}
 | 
			
		||||
		y = 13;
 | 
			
		||||
		i = 0;
 | 
			
		||||
		for(Port &p : sources) {
 | 
			
		||||
			wxColour col =	p.type == GrNode::Port::Type::FILE_OPEN ? wxColour{255, 0, 0}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::FILE_SAVE ? wxColour{255, 0, 0}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::COLOR ? wxColour{0, 0, 255}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::VEC2 ? wxColour{0, 255, 0}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::TEXT ? wxColour{255, 255, 0}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::WINDOW_SOURCE ? wxColour{255, 255, 0}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::SAMPLE ? wxColour{252, 111, 255}
 | 
			
		||||
						:	p.type == GrNode::Port::Type::MIC_SOURCE ? wxColour{255, 255, 255}
 | 
			
		||||
						:	wxColour{128, 128, 128};
 | 
			
		||||
			
 | 
			
		||||
			if(hover && hoverIsSource && i == hoverI) {
 | 
			
		||||
				col.Set(std::min(col.Red() + 50, 255), std::min(col.Green() + 50, 255), std::min(col.Blue() + 50, 255));
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			wxSize sz = dc.GetTextExtent(p.name);
 | 
			
		||||
			dc.SetBrush(wxBrush{col});
 | 
			
		||||
			dc.DrawText(p.name, GetSize().x - sz.x - 15, (y += 20) - sz.y / 2);
 | 
			
		||||
			dc.DrawCircle(GetSize().x - 6, y, 5);
 | 
			
		||||
			
 | 
			
		||||
			i++;
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_LEFT_DOWN, [parent, this](wxMouseEvent &ev){
 | 
			
		||||
		SetFocus();
 | 
			
		||||
		parent->Refresh();
 | 
			
		||||
		
 | 
			
		||||
		bool isSource;
 | 
			
		||||
		int p;
 | 
			
		||||
		if(MouseOverPort(ev.GetPosition(), isSource, p)) {
 | 
			
		||||
			if(parent->attacheeNode && parent->attacheePortIsSource != isSource) {
 | 
			
		||||
				if(parent->attacheePortIsSource) {
 | 
			
		||||
					parent->Alinken({this, p, parent->attacheeNode, parent->attacheePort});
 | 
			
		||||
				} else {
 | 
			
		||||
					parent->Alinken({parent->attacheeNode, parent->attacheePort, this, p});
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				parent->attacheeNode = NULL;
 | 
			
		||||
			} else {
 | 
			
		||||
				parent->attacheeNode = this;
 | 
			
		||||
				parent->attacheePort = p;
 | 
			
		||||
				parent->attacheePortIsSource = isSource;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			CaptureMouse();
 | 
			
		||||
			parent->attacheeNode = NULL;
 | 
			
		||||
			parent->dragged = this;
 | 
			
		||||
			parent->dragPos = ClientToScreen(ev.GetPosition());
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_MOTION, [parent, this](wxMouseEvent &ev){
 | 
			
		||||
		if(wxGetMouseState().LeftIsDown()) {
 | 
			
		||||
			if(HasCapture()) {
 | 
			
		||||
				wxPoint neu = ClientToScreen(ev.GetPosition());
 | 
			
		||||
				SetPosition(GetPosition() + neu - parent->dragPos);
 | 
			
		||||
				parent->dragPos = neu;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		parent->Refresh();
 | 
			
		||||
		SetFocus();
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_LEFT_UP, [parent, this](wxMouseEvent &ev){
 | 
			
		||||
		if(HasCapture()) {
 | 
			
		||||
			ReleaseMouse();
 | 
			
		||||
			parent->dragged = NULL;
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_LEFT_DCLICK, [parent, this](wxMouseEvent &ev){
 | 
			
		||||
		if(ev.GetPosition().y >= 26) {
 | 
			
		||||
			parent->attacheeNode = NULL;
 | 
			
		||||
			
 | 
			
		||||
			int p = (ev.GetPosition().y - 26) / 20;
 | 
			
		||||
			if(p >= (int) sinks.size()) return;
 | 
			
		||||
			
 | 
			
		||||
			if(sinks[p].type == Port::Type::COLOR) {
 | 
			
		||||
				wxColourData data;
 | 
			
		||||
				
 | 
			
		||||
				data.SetChooseFull(true);
 | 
			
		||||
				
 | 
			
		||||
				CHiValue *currentVal = CHi_Crawl(&this->logical->sinks[p]);
 | 
			
		||||
				if(currentVal->type == CUTIHI_VAL_VEC4) {
 | 
			
		||||
					data.SetColour({
 | 
			
		||||
						(uint8_t) (currentVal->data.vec4[0] * 255.f),
 | 
			
		||||
						(uint8_t) (currentVal->data.vec4[1] * 255.f),
 | 
			
		||||
						(uint8_t) (currentVal->data.vec4[2] * 255.f),
 | 
			
		||||
						(uint8_t) (currentVal->data.vec4[3] * 255.f),
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				wxColourDialog dlg(this, &data);
 | 
			
		||||
				if(dlg.ShowModal() == wxID_OK) {
 | 
			
		||||
					CHiValue newv;
 | 
			
		||||
					newv.type = CUTIHI_VAL_VEC4;
 | 
			
		||||
					newv.data.vec4[0] = dlg.GetColourData().GetColour().Red() / 255.f;
 | 
			
		||||
					newv.data.vec4[1] = dlg.GetColourData().GetColour().Green() / 255.f;
 | 
			
		||||
					newv.data.vec4[2] = dlg.GetColourData().GetColour().Blue() / 255.f;
 | 
			
		||||
					newv.data.vec4[3] = dlg.GetColourData().GetColour().Alpha() / 255.f;
 | 
			
		||||
					CHi_ConfigureSink(this->logical, p, newv);
 | 
			
		||||
					parent->Dirtify(this);
 | 
			
		||||
				}
 | 
			
		||||
			} else if(sinks[p].type == Port::Type::FILE_OPEN) {
 | 
			
		||||
				wxFileDialog dlg(this, wxFileSelectorPromptStr, wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_OPEN | wxFD_PREVIEW);
 | 
			
		||||
				if(dlg.ShowModal() == wxID_OK) {
 | 
			
		||||
					CHiValue newv;
 | 
			
		||||
					newv.type = CUTIHI_VAL_TEXT;
 | 
			
		||||
					newv.data.text = strdup(dlg.GetPath().utf8_str());
 | 
			
		||||
					CHi_ConfigureSink(this->logical, p, newv);
 | 
			
		||||
					parent->Dirtify(this);
 | 
			
		||||
				}
 | 
			
		||||
			} else if(sinks[p].type == Port::Type::FILE_SAVE) {
 | 
			
		||||
				wxFileDialog dlg(this, wxFileSelectorPromptStr, wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_SAVE | wxFD_PREVIEW | wxFD_OVERWRITE_PROMPT);
 | 
			
		||||
				if(dlg.ShowModal() == wxID_OK) {
 | 
			
		||||
					CHiValue newv;
 | 
			
		||||
					newv.type = CUTIHI_VAL_TEXT;
 | 
			
		||||
					newv.data.text = strdup(dlg.GetPath().utf8_str());
 | 
			
		||||
					CHi_ConfigureSink(this->logical, p, newv);
 | 
			
		||||
					parent->Dirtify(this);
 | 
			
		||||
				}
 | 
			
		||||
			} else if(sinks[p].type >= Port::Type::VEC1 && sinks[p].type <= Port::Type::VEC4) {
 | 
			
		||||
				auto ctrls = std::make_shared<std::vector<wxTextCtrl*>>();
 | 
			
		||||
				for(int i = 0; i <= (int) sinks[p].type - (int) Port::Type::VEC1; i++) {
 | 
			
		||||
					wxTextCtrl *tc = new wxTextCtrl(GetParent(), wxID_ANY, wxString::Format("%f", this->logical->sinks[p].data.vec4[i]), GetParent()->ScreenToClient(ClientToScreen({5 + 60 * i, (p + 1) * 20})));
 | 
			
		||||
					tc->Bind(wxEVT_KEY_DOWN, [=](wxKeyEvent &ev){
 | 
			
		||||
						if(ev.GetKeyCode() == WXK_RETURN) {
 | 
			
		||||
							double d;
 | 
			
		||||
							if(tc->GetValue().ToDouble(&d)) {
 | 
			
		||||
								CHiValue newv = *CHi_Crawl(&this->logical->sinks[p]);
 | 
			
		||||
								newv.type = CUTIHI_VAL_VEC4;
 | 
			
		||||
								newv.data.vec4[i] = d;
 | 
			
		||||
								CHi_ConfigureSink(this->logical, p, newv);
 | 
			
		||||
							
 | 
			
		||||
								auto it = std::find(ctrls->begin(), ctrls->end(), tc);
 | 
			
		||||
								ctrls->operator[]((it - ctrls->begin() + 1) % ctrls->size())->SetFocus();
 | 
			
		||||
								ctrls->erase(it);
 | 
			
		||||
								
 | 
			
		||||
								CallAfter([tc](){tc->Destroy();});
 | 
			
		||||
								parent->Dirtify(this);
 | 
			
		||||
							}
 | 
			
		||||
						} else if(ev.GetKeyCode() == WXK_TAB) {
 | 
			
		||||
							ctrls->operator[]((i + ctrls->size() + (wxGetKeyState(WXK_SHIFT) ? -1 : 1)) % ctrls->size())->SetFocus();
 | 
			
		||||
							
 | 
			
		||||
							parent->Dirtify(this);
 | 
			
		||||
						} else ev.Skip();
 | 
			
		||||
					});
 | 
			
		||||
					ctrls->push_back(tc);
 | 
			
		||||
				}
 | 
			
		||||
				ctrls->operator[](0)->SetFocus();
 | 
			
		||||
			} else if(sinks[p].type == Port::Type::TEXT) {
 | 
			
		||||
				wxTextCtrl *ctrl = new wxTextCtrl(GetParent(), wxID_ANY, this->logical->sinks[p].data.text, GetParent()->ScreenToClient(ClientToScreen({5, (p + 1) * 26})));
 | 
			
		||||
				ctrl->SetValue(wxString{CHi_Crawl(&this->logical->sinks[p])->data.text});
 | 
			
		||||
				ctrl->SetFocus();
 | 
			
		||||
				ctrl->Bind(wxEVT_KILL_FOCUS, [=](wxFocusEvent &ev){
 | 
			
		||||
					CHiValue newv;
 | 
			
		||||
					newv.type = CUTIHI_VAL_TEXT;
 | 
			
		||||
					char *c = (char*) malloc(ctrl->GetValue().Len() + 1);
 | 
			
		||||
					memcpy(c, ctrl->GetValue().c_str(), ctrl->GetValue().Len() + 1);
 | 
			
		||||
					newv.data.text = c;
 | 
			
		||||
					CHi_ConfigureSink(this->logical, p, newv);
 | 
			
		||||
					CallAfter([ctrl](){ctrl->Destroy();});
 | 
			
		||||
					parent->Dirtify(this);
 | 
			
		||||
				});
 | 
			
		||||
				ctrl->Bind(wxEVT_KEY_DOWN, [=](wxKeyEvent &ev){
 | 
			
		||||
					if(ev.GetKeyCode() == WXK_RETURN) {
 | 
			
		||||
						CHiValue newv;
 | 
			
		||||
						newv.type = CUTIHI_VAL_TEXT;
 | 
			
		||||
						char *c = (char*) malloc(ctrl->GetValue().Len() + 1);
 | 
			
		||||
						memcpy(c, ctrl->GetValue().c_str(), ctrl->GetValue().Len() + 1);
 | 
			
		||||
						newv.data.text = c;
 | 
			
		||||
						CHi_ConfigureSink(this->logical, p, newv);
 | 
			
		||||
						CallAfter([ctrl](){ctrl->Destroy();});
 | 
			
		||||
						parent->Dirtify(this);
 | 
			
		||||
					} else ev.Skip();
 | 
			
		||||
				});
 | 
			
		||||
			} else if(sinks[p].type == Port::Type::MIC_SOURCE) {
 | 
			
		||||
				std::vector<wxString> choices;
 | 
			
		||||
				std::vector<void*> datae;
 | 
			
		||||
				for(size_t i = CHi_Microphone_GetNextSource(-1); i < CHi_Microphone_GetSourceCount(); i = CHi_Microphone_GetNextSource(i)) {
 | 
			
		||||
					choices.push_back(wxString::FromUTF8(CHi_Microphone_GetSourceName(i)));
 | 
			
		||||
					datae.push_back((void*) (uintptr_t) i);
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				wxSingleChoiceDialog dlg(this, "", "Choose Source", choices.size(), choices.data(), datae.data());
 | 
			
		||||
				if(dlg.ShowModal() == wxID_OK) {
 | 
			
		||||
					CHiValue newv;
 | 
			
		||||
					newv.type = CUTIHI_VAL_VEC4;
 | 
			
		||||
					newv.data.vec4[0] = (size_t) (uintptr_t) dlg.GetSelectionData();
 | 
			
		||||
					CHi_ConfigureSink(this->logical, p, newv);
 | 
			
		||||
					parent->Dirtify(this);
 | 
			
		||||
				}
 | 
			
		||||
			} else if(sinks[p].type == Port::Type::WINDOW_SOURCE) {
 | 
			
		||||
				std::vector<const char*> choicesOrig;
 | 
			
		||||
				std::vector<wxString> choices;
 | 
			
		||||
				
 | 
			
		||||
				for(size_t i = CHi_Window_GetNextSource(-1); i < CHi_Window_GetSourceCount(); i = CHi_Window_GetNextSource(i)) {
 | 
			
		||||
					auto name = CHi_Window_GetSourceName(i);
 | 
			
		||||
					
 | 
			
		||||
					choicesOrig.push_back(name);
 | 
			
		||||
					choices.push_back(wxString::FromUTF8(name));
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				wxSingleChoiceDialog dlg(this, "", "Choose Source", choices.size(), choices.data(), (void**) nullptr);
 | 
			
		||||
				if(dlg.ShowModal() == wxID_OK) {
 | 
			
		||||
					CHiValue newv;
 | 
			
		||||
					newv.type = CUTIHI_VAL_TEXT;
 | 
			
		||||
					newv.data.text = strdup(choicesOrig[dlg.GetSelection()]);
 | 
			
		||||
					CHi_ConfigureSink(this->logical, p, newv);
 | 
			
		||||
					parent->Dirtify(this);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_KEY_DOWN, [=](wxKeyEvent &ev){
 | 
			
		||||
		if(ev.GetKeyCode() == 'I') {
 | 
			
		||||
			bool isSource;
 | 
			
		||||
			int p;
 | 
			
		||||
			if(MouseOverPort(ev.GetPosition(), isSource, p) && !isSource) {
 | 
			
		||||
				MakeKeyframe(p);
 | 
			
		||||
			}
 | 
			
		||||
		} else if(ev.GetKeyCode() == WXK_DELETE) {
 | 
			
		||||
			bool isSource;
 | 
			
		||||
			int p;
 | 
			
		||||
			if(MouseOverPort(ev.GetPosition(), isSource, p)) {
 | 
			
		||||
				CHiPubNode *daNode = this->logical;
 | 
			
		||||
				int daPortIdx = p;
 | 
			
		||||
				
 | 
			
		||||
				for(auto it = parent->links.begin(); it != parent->links.end(); it++) {
 | 
			
		||||
					auto &link = *it;
 | 
			
		||||
					if((isSource ? link.output : link.input) == this && (isSource ? link.o : link.i) == p) {
 | 
			
		||||
						parent->links.erase(it);
 | 
			
		||||
						
 | 
			
		||||
						if(isSource) {
 | 
			
		||||
							daNode = link.input->logical;
 | 
			
		||||
							daPortIdx = link.i;
 | 
			
		||||
							isSource = false;
 | 
			
		||||
						}
 | 
			
		||||
						
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				if(!isSource) {
 | 
			
		||||
					CHiValue val;
 | 
			
		||||
					val.type = CUTIHI_VAL_NONE;
 | 
			
		||||
					CHi_ConfigureSink(daNode, daPortIdx, val);
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				parent->Dirtify(this);
 | 
			
		||||
				parent->Refresh();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GrNode::~GrNode() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GrNode::Fit() {
 | 
			
		||||
	SetSize(GetSize().x, (std::max(sinks.size(), sources.size()) + 1) * 23);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ImageViewer::ImageViewer(Frame *f) : wxPanel(f, wxID_ANY) {
 | 
			
		||||
	Bind(wxEVT_PAINT, [this](wxPaintEvent &ev){
 | 
			
		||||
		if(bm.IsOk()) {
 | 
			
		||||
			wxPaintDC dc(this);
 | 
			
		||||
			dc.DrawBitmap(bm, pos);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_MIDDLE_DOWN, [this](wxMouseEvent &ev){
 | 
			
		||||
		CaptureMouse();
 | 
			
		||||
		drag = ev.GetPosition();
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_MOTION, [this](wxMouseEvent &ev){
 | 
			
		||||
		if(HasCapture()) {
 | 
			
		||||
			pos += ev.GetPosition() - drag;
 | 
			
		||||
			drag = ev.GetPosition();
 | 
			
		||||
			Refresh();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_MIDDLE_UP, [this](wxMouseEvent &ev){
 | 
			
		||||
		if(HasCapture()) {
 | 
			
		||||
			ReleaseMouse();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_MOUSEWHEEL, [this](wxMouseEvent &ev){
 | 
			
		||||
		img.SetData((unsigned char*) buf, bufW, bufH, true);
 | 
			
		||||
		ResizeImage(siez + 25 * ev.GetWheelDelta() / ev.GetWheelRotation());
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((optimize("O3"))) static uint8_t *bgra64torgb24(uint8_t *orig, size_t stride, size_t w, size_t h) {
 | 
			
		||||
	auto T0 = wxGetUTCTimeUSec();
 | 
			
		||||
	
 | 
			
		||||
	uint8_t *ret = (uint8_t*) _mm_malloc(w * h * 3 + 16, 16);
 | 
			
		||||
	
 | 
			
		||||
	#pragma omp parallel for
 | 
			
		||||
	for(size_t y = 0; y < h; y++) {
 | 
			
		||||
		
 | 
			
		||||
		uint8_t *temp = orig + stride * y;
 | 
			
		||||
		uint8_t *dest = ret + 3 * w * y;
 | 
			
		||||
		
 | 
			
		||||
		for(size_t x = 0; x < (w & ~15); x += 16, temp += 128, dest += 48) {
 | 
			
		||||
			
 | 
			
		||||
			__m128i z[8] = {};
 | 
			
		||||
			for(int zi = 0; zi < 8; zi++) {
 | 
			
		||||
				z[zi] = _mm_loadu_si128((__m128i*) temp + zi);
 | 
			
		||||
				z[zi] = apply_gamma_epi16(z[zi], _mm_set_ps(1, 1 / 2.2f, 1 / 2.2f, 1 / 2.2f));
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			__m128i a = _mm_shuffle_epi8(z[0], _mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 9, 11, 13, 1, 3, 5));
 | 
			
		||||
			__m128i b = _mm_shuffle_epi8(z[1], _mm_set_epi8(-128, -128, -128, -128, 9, 11, 13, 1, 3, 5, -128, -128, -128, -128, -128, -128));
 | 
			
		||||
			__m128i c = _mm_shuffle_epi8(z[2], _mm_set_epi8(13, 1, 3, 5, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128));
 | 
			
		||||
			__m128i d = _mm_shuffle_epi8(z[2], _mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 9, 11));
 | 
			
		||||
			__m128i e = _mm_shuffle_epi8(z[3], _mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, 9, 11, 13, 1, 3, 5, -128, -128));
 | 
			
		||||
			__m128i f = _mm_shuffle_epi8(z[4], _mm_set_epi8(-128, -128, 9, 11, 13, 1, 3, 5, -128, -128, -128, -128, -128, -128, -128, -128));
 | 
			
		||||
			__m128i g = _mm_shuffle_epi8(z[5], _mm_set_epi8(3, 5, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128));
 | 
			
		||||
			__m128i h = _mm_shuffle_epi8(z[5], _mm_set_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 9, 11, 13, 1));
 | 
			
		||||
			__m128i i = _mm_shuffle_epi8(z[6], _mm_set_epi8(-128, -128, -128, -128, -128, -128, 9, 11, 13, 1, 3, 5, -128, -128, -128, -128));
 | 
			
		||||
			__m128i j = _mm_shuffle_epi8(z[7], _mm_set_epi8(9, 11, 13, 1, 3, 5, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128));
 | 
			
		||||
 | 
			
		||||
			_mm_storeu_si128((__m128i*) dest + 0, _mm_or_si128(_mm_or_si128(a, b), c));
 | 
			
		||||
			_mm_storeu_si128((__m128i*) dest + 1, _mm_or_si128(_mm_or_si128(_mm_or_si128(d, e), f), g));
 | 
			
		||||
			_mm_storeu_si128((__m128i*) dest + 2, _mm_or_si128(_mm_or_si128(h, i), j));
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		for(size_t x = w & ~15; x < w; x++, temp += 8, dest += 3) {
 | 
			
		||||
			
 | 
			
		||||
			uint64_t s = *(uint64_t*) temp;
 | 
			
		||||
			
 | 
			
		||||
			dest[0] = powf(((s >> 40) & 0xFF) / 255.f, 1 / 2.2f) * 255.f;
 | 
			
		||||
			dest[1] = powf(((s >> 24) & 0xFF) / 255.f, 1 / 2.2f) * 255.f;
 | 
			
		||||
			dest[2] = powf(((s >> 8) & 0xFF) / 255.f, 1 / 2.2f) * 255.f;
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	auto T1 = wxGetUTCTimeUSec();
 | 
			
		||||
	
 | 
			
		||||
	printf("%f\n", (T1 - T0).ToDouble() / 1000);
 | 
			
		||||
	
 | 
			
		||||
	 return ret;
 | 
			
		||||
}
 | 
			
		||||
void ImageViewer::SetImage(CHiImage *chim) {
 | 
			
		||||
	if(!chim) return;
 | 
			
		||||
	if(buf) _mm_free(buf);
 | 
			
		||||
	buf = bgra64torgb24((uint8_t*) chim->data16, chim->stride, bufW = chim->width, bufH = chim->height);
 | 
			
		||||
	img.SetData((unsigned char*) buf, chim->width, chim->height, true);
 | 
			
		||||
	ResizeImage(siez);
 | 
			
		||||
}
 | 
			
		||||
void ImageViewer::ResizeImage(float size) {
 | 
			
		||||
	siez = size;
 | 
			
		||||
	img.Rescale(siez, (float) bufH / bufW * siez);
 | 
			
		||||
	bm = wxBitmap(img);
 | 
			
		||||
	Refresh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
 | 
			
		||||
	backendNG = CHi_NewNodeGraph();
 | 
			
		||||
	backendNG->ud = f;
 | 
			
		||||
	
 | 
			
		||||
	{
 | 
			
		||||
		GrNode *v = new GrNode(this);
 | 
			
		||||
		v->logical = CHi_Preview();
 | 
			
		||||
		v->name = "Preview";
 | 
			
		||||
		v->sinks = {{"Video", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
		
 | 
			
		||||
		CHi_RegisterNode(backendNG, v->logical);
 | 
			
		||||
		gnodes.push_back(v);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	Bind(wxEVT_CONTEXT_MENU, [this, f](wxContextMenuEvent &ev){
 | 
			
		||||
		wxMenu menu;
 | 
			
		||||
		int idConstant = menu.Append(wxID_ANY, "Constant", "")->GetId();
 | 
			
		||||
		int idImage = menu.Append(wxID_ANY, "Image", "")->GetId();
 | 
			
		||||
		int idMovie = menu.Append(wxID_ANY, "Movie", "")->GetId();
 | 
			
		||||
		int idWindow = menu.Append(wxID_ANY, "Window", "")->GetId();
 | 
			
		||||
		int idText = menu.Append(wxID_ANY, "Text", "")->GetId();
 | 
			
		||||
		int idMicrophone = menu.Append(wxID_ANY, "Microphone", "")->GetId();
 | 
			
		||||
		int idMixer = menu.Append(wxID_ANY, "Mixer", "")->GetId();
 | 
			
		||||
		int idCamera = menu.Append(wxID_ANY, "Live Digital Camera", "")->GetId();
 | 
			
		||||
		int idTime = menu.Append(wxID_ANY, "Time", "")->GetId();
 | 
			
		||||
		int idEmbed = menu.Append(wxID_ANY, "Embed", "")->GetId();
 | 
			
		||||
		int idComponentScale = menu.Append(wxID_ANY, "Scale", "")->GetId();
 | 
			
		||||
		int idModulate = menu.Append(wxID_ANY, "Modulate", "")->GetId();
 | 
			
		||||
		int idKeyhook = menu.Append(wxID_ANY, "Keyhook (Live)", "")->GetId();
 | 
			
		||||
		int idEncodeVp8 = menu.Append(wxID_ANY, "Encode VP8", "")->GetId();
 | 
			
		||||
		int idEncodeVp9 = menu.Append(wxID_ANY, "Encode VP9", "")->GetId();
 | 
			
		||||
		int idEncodeOpus = menu.Append(wxID_ANY, "Encode Opus", "")->GetId();
 | 
			
		||||
		int idMuxWebm = menu.Append(wxID_ANY, "Mux WebM", "")->GetId();
 | 
			
		||||
		int idMuxWav = menu.Append(wxID_ANY, "Muv Wav", "")->GetId();
 | 
			
		||||
		wxPoint position = ScreenToClient(wxGetMousePosition());
 | 
			
		||||
		
 | 
			
		||||
		menu.Bind(wxEVT_MENU, [=](wxCommandEvent &ev){
 | 
			
		||||
			std::function<void()> after = [](){};
 | 
			
		||||
			
 | 
			
		||||
			GrNode *noed = nullptr;
 | 
			
		||||
			if(ev.GetId() == idConstant) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_ConstantSample();
 | 
			
		||||
				printf("%p\n", noed->logical->sinks[0].data.vec4);
 | 
			
		||||
				noed->name = "Constant";
 | 
			
		||||
				noed->sinks = {{"Color", GrNode::Port::Type::COLOR}};
 | 
			
		||||
				noed->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
			} else if(ev.GetId() == idImage) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Image();
 | 
			
		||||
				noed->name = "Image";
 | 
			
		||||
				noed->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPEN}};
 | 
			
		||||
				noed->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
			} else if(ev.GetId() == idEmbed) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Embed();
 | 
			
		||||
				noed->name = "Embed";
 | 
			
		||||
				noed->sinks = {
 | 
			
		||||
					{"Frame", GrNode::Port::Type::SAMPLE},
 | 
			
		||||
					{"Sub 1", GrNode::Port::Type::SAMPLE}, {" Pos", GrNode::Port::Type::VEC2}, {" Size", GrNode::Port::Type::VEC1},
 | 
			
		||||
					{"Sub 2", GrNode::Port::Type::SAMPLE}, {" Pos", GrNode::Port::Type::VEC2}, {" Size", GrNode::Port::Type::VEC1},
 | 
			
		||||
					{"Sub 3", GrNode::Port::Type::SAMPLE}, {" Pos", GrNode::Port::Type::VEC2}, {" Size", GrNode::Port::Type::VEC1}
 | 
			
		||||
				};
 | 
			
		||||
				noed->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
			} else if(ev.GetId() == idModulate) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Modulate();
 | 
			
		||||
				noed->name = "Modulate";
 | 
			
		||||
				noed->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}, {"Brightness", GrNode::Port::Type::VEC1}, {"Contrast", GrNode::Port::Type::VEC1}, {"Hue", GrNode::Port::Type::VEC1}};
 | 
			
		||||
				noed->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
			} else if(ev.GetId() == idMovie) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Movie();
 | 
			
		||||
				noed->name = "Movie";
 | 
			
		||||
				noed->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPEN}, {"Time", GrNode::Port::Type::VEC1}};
 | 
			
		||||
				noed->sources = {{"Sample", GrNode::Port::Type::SAMPLE}, {"Audio", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
				
 | 
			
		||||
				after = [=](){
 | 
			
		||||
					size_t portIdx = std::distance(noed->sinks.begin(), std::find_if(noed->sinks.begin(), noed->sinks.end(), [](GrNode::Port& p) -> bool {
 | 
			
		||||
						return p.name == "Time";
 | 
			
		||||
					}));
 | 
			
		||||
					
 | 
			
		||||
					CHi_MakeKeyframe(backendNG, noed->logical, portIdx);
 | 
			
		||||
					
 | 
			
		||||
					float params[4] = {1};
 | 
			
		||||
					CHi_SetExtrapolationMode(backendNG, noed->logical, portIdx, CUTIHI_EXTRAPOLATION_CONSTANT, params);
 | 
			
		||||
				};
 | 
			
		||||
			} else if(ev.GetId() == idEncodeVp9) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_EncodeVP9();
 | 
			
		||||
				noed->name = "Encode VP9";
 | 
			
		||||
				noed->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
				noed->sources = {{"Bitstream"}};
 | 
			
		||||
			} else if(ev.GetId() == idEncodeVp8) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_EncodeVP8();
 | 
			
		||||
				noed->name = "Encode VP8";
 | 
			
		||||
				noed->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
				noed->sources = {{"Bitstream"}};
 | 
			
		||||
			} else if(ev.GetId() == idMuxWebm) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_MuxWebm();
 | 
			
		||||
				noed->name = "Mux WebM";
 | 
			
		||||
				noed->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVE}};
 | 
			
		||||
				noed->sources = {};
 | 
			
		||||
			} else if(ev.GetId() == idWindow) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Window();
 | 
			
		||||
				noed->name = "Window";
 | 
			
		||||
				noed->sinks = {{"Name", GrNode::Port::Type::WINDOW_SOURCE}};
 | 
			
		||||
				noed->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
			} else if(ev.GetId() == idText) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Text();
 | 
			
		||||
				noed->name = "Text";
 | 
			
		||||
				noed->sinks = {{"Text", GrNode::Port::Type::TEXT}, {"Color", GrNode::Port::Type::COLOR}, {"DPI", GrNode::Port::Type::VEC1}};
 | 
			
		||||
				noed->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
			} else if(ev.GetId() == idTime) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Time();
 | 
			
		||||
				noed->name = "Time";
 | 
			
		||||
				noed->sinks = {};
 | 
			
		||||
				noed->sources = {{"t", GrNode::Port::Type::VEC1}};
 | 
			
		||||
			} else if(ev.GetId() == idMicrophone) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Microphone();
 | 
			
		||||
				noed->name = "Microphone";
 | 
			
		||||
				noed->sinks = {{"Source", GrNode::Port::Type::MIC_SOURCE}};
 | 
			
		||||
				noed->sources = {{"Audio", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
			} else if(ev.GetId() == idMixer) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Mixer();
 | 
			
		||||
				noed->name = "Mixer";
 | 
			
		||||
				noed->sinks = {{"Sink 1", GrNode::Port::Type::SAMPLE}, {"Sink 2", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
				noed->sources = {{"Audio", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
			} else if(ev.GetId() == idMuxWav) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_ExportWav();
 | 
			
		||||
				noed->name = "Mux Wav";
 | 
			
		||||
				noed->sinks = {{"Filename", GrNode::Port::Type::FILE_SAVE}, {"Audio", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
				noed->sources = {};
 | 
			
		||||
			} else if(ev.GetId() == idEncodeOpus) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_EncodeOpus();
 | 
			
		||||
				noed->name = "Encode Opus";
 | 
			
		||||
				noed->sinks = {{"Audio", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
				noed->sources = {{"Bitstream"}};
 | 
			
		||||
			} else if(ev.GetId() == idCamera) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Camera();
 | 
			
		||||
				noed->name = "Live Digital Camera";
 | 
			
		||||
				noed->sinks = {};
 | 
			
		||||
				noed->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
			} else if(ev.GetId() == idComponentScale) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_ComponentScale();
 | 
			
		||||
				noed->name = "Scale";
 | 
			
		||||
				noed->sinks = {{"Vector", GrNode::Port::Type::VEC4}, {"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
				noed->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
 | 
			
		||||
			} else if(ev.GetId() == idKeyhook) {
 | 
			
		||||
				noed = new GrNode(this);
 | 
			
		||||
				noed->logical = CHi_Keyhook();
 | 
			
		||||
				noed->name = "Keyhook";
 | 
			
		||||
				noed->sinks = {{"Key", GrNode::Port::Type::VEC1}, {"Smooth Time", GrNode::Port::Type::VEC1}};
 | 
			
		||||
				noed->sources = {{"Bool", GrNode::Port::Type::VEC1}};
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			if(noed) {
 | 
			
		||||
				noed->Fit();
 | 
			
		||||
				noed->SetPosition(position);
 | 
			
		||||
				CHi_RegisterNode(backendNG, noed->logical);
 | 
			
		||||
				gnodes.push_back(noed);
 | 
			
		||||
				
 | 
			
		||||
				after();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		PopupMenu(&menu);
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &ev){
 | 
			
		||||
		SetFocusIgnoringChildren();
 | 
			
		||||
		Refresh();
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &ev){
 | 
			
		||||
		if(attacheeNode) attacheeNode = NULL;
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	Bind(wxEVT_MIDDLE_DOWN, [this](wxMouseEvent &ev){
 | 
			
		||||
		dragged = nullptr;
 | 
			
		||||
		dragPos = ClientToScreen(ev.GetPosition());
 | 
			
		||||
		CaptureMouse();
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_MIDDLE_UP, [this](wxMouseEvent &ev){
 | 
			
		||||
		if(HasCapture()) {
 | 
			
		||||
			ReleaseMouse();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_MOTION, [this](wxMouseEvent &ev) {
 | 
			
		||||
		Refresh();
 | 
			
		||||
		
 | 
			
		||||
		if(HasCapture()) {
 | 
			
		||||
			wxPoint neu = ClientToScreen(ev.GetPosition());
 | 
			
		||||
			for(auto gr : gnodes) {
 | 
			
		||||
				gr->SetPosition(gr->GetPosition() + neu - dragPos);
 | 
			
		||||
			}
 | 
			
		||||
			dragPos = neu;
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	Bind(wxEVT_PAINT, [this](wxPaintEvent &ev) {
 | 
			
		||||
		wxPaintDC dc(this);
 | 
			
		||||
		
 | 
			
		||||
		wxPoint p[2];
 | 
			
		||||
		for(Link l : links) {
 | 
			
		||||
			p[0] = l.input->GetPosition() + wxPoint{0, 33 + 20 * l.i};
 | 
			
		||||
			p[1] = l.output->GetPosition() + wxPoint{l.output->GetSize().x, 33 + 20 * l.o};
 | 
			
		||||
			dc.DrawSpline(2, p);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		if(attacheeNode) {
 | 
			
		||||
			p[0] = attacheeNode->GetPosition() + wxPoint{attacheePortIsSource ? attacheeNode->GetSize().x : 0, 33 + 20 * attacheePort};
 | 
			
		||||
			p[1] = ScreenToClient(wxGetMousePosition());
 | 
			
		||||
			dc.DrawSpline(2, p);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NodeGraph::~NodeGraph() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NodeGraph::Alinken(Link l) {
 | 
			
		||||
	CHiValue newv;
 | 
			
		||||
	newv.type = CUTIHI_VAL_LINKED;
 | 
			
		||||
	newv.data.linked.to = l.output->logical;
 | 
			
		||||
	newv.data.linked.idx = l.o;
 | 
			
		||||
	
 | 
			
		||||
	if(!CHi_ConfigureSink(l.input->logical, l.i, newv)) {
 | 
			
		||||
		((Frame*) GetParent())->stba->SetStatusText("Uh oh! Hur-hur, there's a WAACKY cycle! Can't do, sorry friend.");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	for(auto it = links.begin(); it != links.end(); it++) {
 | 
			
		||||
		if((*it).input == l.input && (*it).i == l.i) {
 | 
			
		||||
			links.erase(it);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	links.push_back(l);
 | 
			
		||||
	
 | 
			
		||||
	Dirtify(l.input);
 | 
			
		||||
	Refresh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NodeGraph::Dirtify(GrNode *g) {
 | 
			
		||||
	g->logical->clean = 0;
 | 
			
		||||
	for(auto &it : links) {
 | 
			
		||||
		if(it.output == g) {
 | 
			
		||||
			Dirtify(it.input);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if(g == gnodes[0]) {
 | 
			
		||||
		if(CHi_Hysteresis(g->logical)) {
 | 
			
		||||
			CHiValue *val = CHi_Crawl(&g->logical->sinks[0]);
 | 
			
		||||
			
 | 
			
		||||
			if(val->type == CUTIHI_VAL_SAMPLE && val->data.sample) {
 | 
			
		||||
				((Frame*) GetParent())->viewer->SetImage(val->data.sample);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool operator==(const NodeGraph::Link &l, const NodeGraph::Link &r) {
 | 
			
		||||
	return l.input == r.input && l.i == r.i && l.output == r.output && l.o == r.o;
 | 
			
		||||
}
 | 
			
		||||
static bool dfs(NodeGraph *ng, std::set<GrNode*> &p, GrNode *g) {
 | 
			
		||||
	p.insert(g);
 | 
			
		||||
	
 | 
			
		||||
	bool cyclic = false;
 | 
			
		||||
	for(const NodeGraph::Link &l : ng->links) {
 | 
			
		||||
		if(l.output == g && (std::find(p.begin(), p.end(), g) != p.end() || dfs(ng, p, l.input))) {
 | 
			
		||||
			cyclic = true;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	p.erase(std::find(p.begin(), p.end(), g));
 | 
			
		||||
	
 | 
			
		||||
	return cyclic;
 | 
			
		||||
}
 | 
			
		||||
bool NodeGraph::DetectCycles(GrNode *root) {
 | 
			
		||||
	std::set<GrNode*> p{};
 | 
			
		||||
	return dfs(this, p, root);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CompositionSettings::CompositionSettings(Frame *parent) : wxPanel(parent, wxID_ANY) {
 | 
			
		||||
	auto sz = new wxBoxSizer(wxVERTICAL);
 | 
			
		||||
	
 | 
			
		||||
	sz->Add(this->durationEnable = new wxCheckBox(this, wxID_ANY, "Duration"), 0, wxALIGN_CENTER);
 | 
			
		||||
	sz->Add(this->duration = new ctTimeCtrl(this, 120), 0, wxEXPAND);
 | 
			
		||||
	
 | 
			
		||||
	durationEnable->SetValue(true);
 | 
			
		||||
	durationEnable->Bind(wxEVT_CHECKBOX, [=](wxCommandEvent&){
 | 
			
		||||
		duration->Enable(durationEnable->GetValue());
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	sz->Add(this->btnPerform = new wxButton(this, wxID_ANY, "Compile"), 0, wxEXPAND);
 | 
			
		||||
	btnPerform->Bind(wxEVT_BUTTON, [=](wxCommandEvent &ev){
 | 
			
		||||
		if(btnPerform->GetLabel() == "Kill") {
 | 
			
		||||
			CHi_StopCompilation(parent->graph->backendNG);
 | 
			
		||||
			btnPerform->Disable();
 | 
			
		||||
		} else {
 | 
			
		||||
			CHi_SetDuration(parent->graph->backendNG, durationEnable->IsChecked() ? duration->GetSeconds() : -1);
 | 
			
		||||
			CHi_BeginCompilation(parent->graph->backendNG);
 | 
			
		||||
			btnPerform->SetLabel("Kill");
 | 
			
		||||
			
 | 
			
		||||
			std::thread{[=](){
 | 
			
		||||
				while(parent->graph->backendNG->compilationStatus == CUTIHI_COMP_RUNNING) {
 | 
			
		||||
					parent->CallAfter([=](){
 | 
			
		||||
						float t = CHi_Time_Get(parent->graph->backendNG);
 | 
			
		||||
						parent->stba->SetStatusText(wxString::Format("%02i:%02i:%06.03fs", (int) (t / 3600), (int) (t / 60), fmodf(t, 60)));
 | 
			
		||||
					});
 | 
			
		||||
					
 | 
			
		||||
					std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				parent->CallAfter([=](){
 | 
			
		||||
					parent->stba->SetStatusText("Compilation has ended.");
 | 
			
		||||
				});
 | 
			
		||||
			}}.detach();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	parent->graph->backendNG->eventOnStopComplete = +[](CHiNodeGraph *ng){
 | 
			
		||||
		wxTheApp->CallAfter([ng](){
 | 
			
		||||
			wxButton *btn = ((Frame*) ng->ud)->compsets->btnPerform;
 | 
			
		||||
			btn->Enable();
 | 
			
		||||
			btn->SetLabel("Compile");
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	SetSizerAndFit(sz);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								ui/frame.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								ui/frame.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,131 @@
 | 
			
		||||
#ifndef _CUTICLE_FRAME_H
 | 
			
		||||
#define _CUTICLE_FRAME_H
 | 
			
		||||
 | 
			
		||||
#include<vector>
 | 
			
		||||
#include<set>
 | 
			
		||||
#include<wx/frame.h>
 | 
			
		||||
#include<wx/panel.h>
 | 
			
		||||
#include<wx/aui/aui.h>
 | 
			
		||||
#include<wx/button.h>
 | 
			
		||||
#include<wx/spinctrl.h>
 | 
			
		||||
#include<wx/statusbr.h>
 | 
			
		||||
#include<wx/toolbar.h>
 | 
			
		||||
#include<wx/checkbox.h>
 | 
			
		||||
 | 
			
		||||
#include<hi/node.h>
 | 
			
		||||
 | 
			
		||||
#include<hi/img.h>
 | 
			
		||||
 | 
			
		||||
struct NodeGraph;
 | 
			
		||||
struct ImageViewer;
 | 
			
		||||
struct CompositionSettings;
 | 
			
		||||
struct Timeline;
 | 
			
		||||
struct Frame : wxFrame {
 | 
			
		||||
	wxAuiManager aui;
 | 
			
		||||
	
 | 
			
		||||
	ImageViewer *viewer;
 | 
			
		||||
	NodeGraph *graph;
 | 
			
		||||
	CompositionSettings *compsets;
 | 
			
		||||
	Timeline *timeline;
 | 
			
		||||
	
 | 
			
		||||
	wxStatusBar *stba;
 | 
			
		||||
	wxToolBar *tlba;
 | 
			
		||||
	
 | 
			
		||||
	Frame();
 | 
			
		||||
	virtual ~Frame();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GrNode : wxPanel {
 | 
			
		||||
	struct Port {
 | 
			
		||||
		wxString name;
 | 
			
		||||
		enum class Type {
 | 
			
		||||
			NONE, FILE_OPEN, COLOR, VEC1, VEC2, VEC3, VEC4, TEXT, SAMPLE, FILE_SAVE, MIC_SOURCE, WINDOW_SOURCE
 | 
			
		||||
		} type;
 | 
			
		||||
	};
 | 
			
		||||
	std::vector<Port> sinks;
 | 
			
		||||
	std::vector<Port> sources;
 | 
			
		||||
	
 | 
			
		||||
	wxString name;
 | 
			
		||||
	
 | 
			
		||||
	CHiPubNode *logical;
 | 
			
		||||
	
 | 
			
		||||
	GrNode(NodeGraph*);
 | 
			
		||||
	virtual ~GrNode();
 | 
			
		||||
	
 | 
			
		||||
	bool MouseOverPort(wxPoint p, bool &source, int &i);
 | 
			
		||||
	
 | 
			
		||||
	void MakeKeyframe(int i);
 | 
			
		||||
	
 | 
			
		||||
	virtual void Fit() override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ctTimeCtrl;
 | 
			
		||||
struct CompositionSettings : wxPanel {
 | 
			
		||||
	ctTimeCtrl *duration;
 | 
			
		||||
	wxCheckBox *durationEnable;
 | 
			
		||||
	
 | 
			
		||||
	wxButton *btnPerform;
 | 
			
		||||
	
 | 
			
		||||
	CompositionSettings(Frame*);
 | 
			
		||||
	virtual ~CompositionSettings() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ImageViewer : wxPanel {
 | 
			
		||||
	wxPoint pos;
 | 
			
		||||
	wxImage img;
 | 
			
		||||
	wxBitmap bm;
 | 
			
		||||
	size_t bufW, bufH;
 | 
			
		||||
	uint8_t *buf = nullptr;
 | 
			
		||||
	float siez = 512;
 | 
			
		||||
	
 | 
			
		||||
	wxPoint drag;
 | 
			
		||||
	
 | 
			
		||||
	ImageViewer(Frame*);
 | 
			
		||||
	virtual ~ImageViewer() = default;
 | 
			
		||||
	
 | 
			
		||||
	void SetImage(CHiImage *img);
 | 
			
		||||
	void ResizeImage(float);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct NodeGraph : wxPanel {
 | 
			
		||||
	struct Link {
 | 
			
		||||
		GrNode *input;
 | 
			
		||||
		int i;
 | 
			
		||||
		GrNode *output;
 | 
			
		||||
		int o;
 | 
			
		||||
		
 | 
			
		||||
		struct Comparator {
 | 
			
		||||
			bool operator ()(const Link &a, const Link &b) {
 | 
			
		||||
				if(a.input != b.input) return a.input < b.input;
 | 
			
		||||
				else if(a.i != b.i) return a.i < b.i;
 | 
			
		||||
				else if(a.output != b.output) return a.output < b.output;
 | 
			
		||||
				else return a.o < b.o;
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
	
 | 
			
		||||
	GrNode *attacheeNode = NULL;
 | 
			
		||||
	int attacheePort;
 | 
			
		||||
	int attacheePortIsSource;
 | 
			
		||||
	
 | 
			
		||||
	GrNode *dragged = NULL;
 | 
			
		||||
	wxPoint dragPos;
 | 
			
		||||
	
 | 
			
		||||
	CHiNodeGraph *backendNG = NULL;
 | 
			
		||||
	
 | 
			
		||||
	std::vector<GrNode*> gnodes;
 | 
			
		||||
	std::vector<Link> links;
 | 
			
		||||
	
 | 
			
		||||
	NodeGraph(Frame*);
 | 
			
		||||
	virtual ~NodeGraph();
 | 
			
		||||
	
 | 
			
		||||
	void Alinken(Link l);
 | 
			
		||||
	
 | 
			
		||||
	void Dirtify(GrNode *g);
 | 
			
		||||
	
 | 
			
		||||
	bool DetectCycles(GrNode*);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										12
									
								
								ui/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								ui/main.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
#include<wx/wx.h>
 | 
			
		||||
 | 
			
		||||
#include"frame.h"
 | 
			
		||||
 | 
			
		||||
struct App : wxApp {
 | 
			
		||||
	virtual bool OnInit() {
 | 
			
		||||
		(new Frame())->Show(true);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
wxIMPLEMENT_APP(App);
 | 
			
		||||
							
								
								
									
										46
									
								
								ui/textctrl.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								ui/textctrl.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
#include"timectrl.h"
 | 
			
		||||
 | 
			
		||||
ctTimeCtrl::ctTimeCtrl(wxWindow *parent, double seconds) : wxTextCtrl(parent, wxID_ANY) {
 | 
			
		||||
	SetSeconds(seconds);
 | 
			
		||||
	
 | 
			
		||||
	Bind(wxEVT_CHAR, [this](wxKeyEvent& ev){
 | 
			
		||||
		if(ev.GetKeyCode() == WXK_LEFT || ev.GetKeyCode() == WXK_RIGHT) ev.Skip();
 | 
			
		||||
		else if(ev.GetKeyCode() >= '0' && ev.GetKeyCode() <= '9') {
 | 
			
		||||
			uint32_t c = GetValue()[GetInsertionPoint()].GetValue();
 | 
			
		||||
			if(c >= '0' && c <= '9') {
 | 
			
		||||
				// SetValue calls EVT_TEXT
 | 
			
		||||
				wxString neu = GetValue().Clone();
 | 
			
		||||
				neu.SetChar(GetInsertionPoint(), ev.GetUnicodeKey());
 | 
			
		||||
				SetValue(neu);
 | 
			
		||||
				
 | 
			
		||||
				CallAfter([this](){
 | 
			
		||||
					SetInsertionPoint(GetInsertionPoint() + 1);
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		} else if(GetInsertionPoint() < (long) GetValue().Length() && GetValue()[GetInsertionPoint()].GetValue() == ev.GetUnicodeKey()) {
 | 
			
		||||
			CallAfter([this](){
 | 
			
		||||
				SetInsertionPoint(GetInsertionPoint() + 1);
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_TEXT, [this](wxCommandEvent &ev){
 | 
			
		||||
		long h, m;
 | 
			
		||||
		double s;
 | 
			
		||||
		
 | 
			
		||||
		GetValue().ToCLong(&h);
 | 
			
		||||
		GetValue().Mid(3).ToCLong(&m);
 | 
			
		||||
		GetValue().Mid(6).ToCDouble(&s);
 | 
			
		||||
		
 | 
			
		||||
		this->seconds = (h * 60 + m) * 60 + s;
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double ctTimeCtrl::GetSeconds() {
 | 
			
		||||
	return seconds;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ctTimeCtrl::SetSeconds(double seconds) {
 | 
			
		||||
	this->seconds = seconds;
 | 
			
		||||
	
 | 
			
		||||
	ChangeValue(wxString::Format("%02i:%02i:%02.3g", (int) (seconds / 3600), (int) (seconds / 60), fmodf(seconds, 60)));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								ui/timectrl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								ui/timectrl.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include<wx/textctrl.h>
 | 
			
		||||
 | 
			
		||||
struct ctTimeCtrl : wxTextCtrl {
 | 
			
		||||
	ctTimeCtrl(wxWindow *parent, double seconds);
 | 
			
		||||
	virtual ~ctTimeCtrl() = default;
 | 
			
		||||
	
 | 
			
		||||
	double GetSeconds();
 | 
			
		||||
	void SetSeconds(double);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	double seconds;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										273
									
								
								ui/timeline.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								ui/timeline.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,273 @@
 | 
			
		||||
#include"timeline.h"
 | 
			
		||||
 | 
			
		||||
#include<wx/menu.h>
 | 
			
		||||
#include<wx/dcclient.h>
 | 
			
		||||
#include<wx/settings.h>
 | 
			
		||||
 | 
			
		||||
#include<algorithm>
 | 
			
		||||
 | 
			
		||||
#include<float.h>
 | 
			
		||||
 | 
			
		||||
#include"frame.h"
 | 
			
		||||
 | 
			
		||||
static wxBitmap bmpKf;
 | 
			
		||||
static wxBitmap bmpKfExtrap;
 | 
			
		||||
 | 
			
		||||
template<typename T>
 | 
			
		||||
static T mod(T a, T b) {
 | 
			
		||||
	return (a % b + b) % b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define ZERO_TIME_BASE 128
 | 
			
		||||
 | 
			
		||||
bool Timeline::MouseOverKF(wxPoint p, size_t &kfsIdxRet, size_t &kfIdxRet) {
 | 
			
		||||
	auto f = (Frame*) GetParent();
 | 
			
		||||
	
 | 
			
		||||
	int kfsIdx = p.y / bmpKf.GetHeight() - 1;
 | 
			
		||||
	
 | 
			
		||||
	if(kfsIdx < 0 || kfsIdx >= f->graph->backendNG->keyframesList.count) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	float t = (p.x + camX - ZERO_TIME_BASE) / (float) scale;
 | 
			
		||||
	float threshold = bmpKf.GetWidth() / (float) scale / 2;
 | 
			
		||||
	
 | 
			
		||||
	size_t idx = CHi_GetClosestKeyframe(f->graph->backendNG, kfsIdx, t);
 | 
			
		||||
	
 | 
			
		||||
	if(fabs(f->graph->backendNG->keyframesList.keyframes[kfsIdx]->times[idx] - t) > threshold) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	kfsIdxRet = kfsIdx;
 | 
			
		||||
	kfIdxRet = idx;
 | 
			
		||||
	
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Timeline::Timeline(struct Frame *parent) : wxPanel(parent, wxID_ANY) {
 | 
			
		||||
	bmpKf = wxBitmap{"keyframe.bmp", wxBITMAP_TYPE_ANY};
 | 
			
		||||
	bmpKfExtrap = wxBitmap{"keyframe_extrap.bmp", wxBITMAP_TYPE_ANY};
 | 
			
		||||
	
 | 
			
		||||
	Bind(wxEVT_PAINT, &Timeline::Paint, this);
 | 
			
		||||
	
 | 
			
		||||
	Bind(wxEVT_MIDDLE_DOWN, [=](wxMouseEvent &ev){
 | 
			
		||||
		captureMode = Timeline::CaptureMode::CAM;
 | 
			
		||||
		CaptureMouse();
 | 
			
		||||
		mouseX = ev.GetX();
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_MIDDLE_UP, [=](wxMouseEvent &ev){
 | 
			
		||||
		if(HasCapture() && captureMode == Timeline::CaptureMode::CAM) {
 | 
			
		||||
			ReleaseMouse();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	Bind(wxEVT_LEFT_DOWN, [=](wxMouseEvent &ev){
 | 
			
		||||
		auto f = (Frame*) GetParent();
 | 
			
		||||
		
 | 
			
		||||
		size_t kfsIdx, kfIdx;
 | 
			
		||||
		if(MouseOverKF(ev.GetPosition(), kfsIdx, kfIdx)) {
 | 
			
		||||
			captureMode = Timeline::CaptureMode::KF;
 | 
			
		||||
			CaptureMouse();
 | 
			
		||||
			mouseX = ev.GetX();
 | 
			
		||||
			
 | 
			
		||||
			this->captureKfsIdx = kfsIdx;
 | 
			
		||||
			this->captureKfIdx = kfIdx;
 | 
			
		||||
		} else {
 | 
			
		||||
			float t = (ev.GetX() + camX - ZERO_TIME_BASE) / (float) scale;
 | 
			
		||||
			
 | 
			
		||||
			// Snap to closest keyframe, in all keyframes
 | 
			
		||||
			if(ev.ControlDown()) {
 | 
			
		||||
				float minDist = FLT_MAX;
 | 
			
		||||
				float newT = t;
 | 
			
		||||
				
 | 
			
		||||
				for(size_t kfsIdx = 0; kfsIdx < f->graph->backendNG->keyframesList.count; kfsIdx++) {
 | 
			
		||||
					size_t kfIdx = CHi_GetClosestKeyframe(f->graph->backendNG, kfsIdx, t);
 | 
			
		||||
					
 | 
			
		||||
					float dist = fabs(f->graph->backendNG->keyframesList.keyframes[kfsIdx]->times[kfIdx] - t);
 | 
			
		||||
					
 | 
			
		||||
					if(dist < minDist) {
 | 
			
		||||
						minDist = dist;
 | 
			
		||||
						newT = f->graph->backendNG->keyframesList.keyframes[kfsIdx]->times[kfIdx];
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				t = newT;
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			CHi_Time_Set(f->graph->backendNG, t < 0 ? 0 : t);
 | 
			
		||||
			Refresh();
 | 
			
		||||
			
 | 
			
		||||
			f->graph->Dirtify(f->graph->gnodes[0]);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	Bind(wxEVT_LEFT_UP, [=](wxMouseEvent &ev){
 | 
			
		||||
		if(HasCapture() && captureMode == Timeline::CaptureMode::KF) {
 | 
			
		||||
			ReleaseMouse();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	Bind(wxEVT_MOTION, [=](wxMouseEvent &ev){
 | 
			
		||||
		auto f = (Frame*) GetParent();
 | 
			
		||||
		
 | 
			
		||||
		if(HasCapture()) {
 | 
			
		||||
			if(captureMode == Timeline::CaptureMode::CAM) {
 | 
			
		||||
				camX += mouseX - ev.GetX();
 | 
			
		||||
				if(camX < 0) {
 | 
			
		||||
					camX = 0;
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				Refresh();
 | 
			
		||||
			} else if(captureMode == Timeline::CaptureMode::KF) {
 | 
			
		||||
				int64_t diff = ev.GetX() - mouseX;
 | 
			
		||||
				
 | 
			
		||||
				float timeDiff = (float) diff / this->scale;
 | 
			
		||||
				
 | 
			
		||||
				captureKfIdx = CHi_MoveKeyframeBy(f->graph->backendNG, f->graph->backendNG->keyframesList.keyframes[captureKfsIdx], captureKfIdx, timeDiff);
 | 
			
		||||
				
 | 
			
		||||
				Refresh();
 | 
			
		||||
				
 | 
			
		||||
				f->graph->Dirtify(f->graph->gnodes[0]);
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			mouseX = ev.GetX();
 | 
			
		||||
		} else {
 | 
			
		||||
			// This is really baad..
 | 
			
		||||
			
 | 
			
		||||
			size_t kfsIdx, kfIdx;
 | 
			
		||||
			if(GetToolTipText() == "" && MouseOverKF(ScreenToClient(wxGetMousePosition()), kfsIdx, kfIdx)) {
 | 
			
		||||
				CHiKeyframes *kfs = f->graph->backendNG->keyframesList.keyframes[kfsIdx];
 | 
			
		||||
				
 | 
			
		||||
				CHiPubNode *node = kfs->node;
 | 
			
		||||
				
 | 
			
		||||
				auto it = std::find_if(f->graph->gnodes.begin(), f->graph->gnodes.end(), [=](GrNode *g){
 | 
			
		||||
					return g->logical == node;
 | 
			
		||||
				});
 | 
			
		||||
				
 | 
			
		||||
				assert(it != f->graph->gnodes.end());
 | 
			
		||||
				
 | 
			
		||||
				auto &sinks = (*it)->sinks;
 | 
			
		||||
				
 | 
			
		||||
				for(size_t i = 0; i < node->sinkCount; i++) {
 | 
			
		||||
					if(node->sinks[i].type == CUTIHI_VAL_KEYED && node->sinks[i].data.keyed == kfs) {
 | 
			
		||||
						
 | 
			
		||||
						CHiValueRaw *val = &node->sinks[i].data.keyed->values[kfIdx];
 | 
			
		||||
						
 | 
			
		||||
						switch(sinks[i].type) {
 | 
			
		||||
						case GrNode::Port::Type::TEXT:
 | 
			
		||||
						case GrNode::Port::Type::FILE_OPEN:
 | 
			
		||||
						case GrNode::Port::Type::FILE_SAVE:
 | 
			
		||||
							SetToolTip(wxString{val->text});
 | 
			
		||||
							break;
 | 
			
		||||
						case GrNode::Port::Type::VEC1:
 | 
			
		||||
							SetToolTip(wxString::Format("%g", val->vec4[0]));
 | 
			
		||||
							break;
 | 
			
		||||
						case GrNode::Port::Type::VEC2:
 | 
			
		||||
							SetToolTip(wxString::Format("(%g, %g)", val->vec4[0], val->vec4[1]));
 | 
			
		||||
							break;
 | 
			
		||||
						case GrNode::Port::Type::VEC3:
 | 
			
		||||
							SetToolTip(wxString::Format("(%g, %g, %g)", val->vec4[0], val->vec4[1], val->vec4[2]));
 | 
			
		||||
							break;
 | 
			
		||||
						case GrNode::Port::Type::VEC4:
 | 
			
		||||
						case GrNode::Port::Type::COLOR:
 | 
			
		||||
							SetToolTip(wxString::Format("(%g, %g, %g, %g)", val->vec4[0], val->vec4[1], val->vec4[2], val->vec4[3]));
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							SetToolTip(" ");
 | 
			
		||||
						}
 | 
			
		||||
						
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				SetToolTip(nullptr);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	Bind(wxEVT_MOUSEWHEEL, [=](wxMouseEvent &ev){
 | 
			
		||||
		int delta = ev.GetWheelRotation() / ev.GetWheelDelta();
 | 
			
		||||
		
 | 
			
		||||
		while(delta > 0) {
 | 
			
		||||
			scale *= 2;
 | 
			
		||||
			if(scale > 800) {
 | 
			
		||||
				scale = 800;
 | 
			
		||||
			}
 | 
			
		||||
			delta--;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		while(delta < 0) {
 | 
			
		||||
			scale /= 2;
 | 
			
		||||
			if(scale < 25) {
 | 
			
		||||
				scale = 25;
 | 
			
		||||
			}
 | 
			
		||||
			delta++;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		Refresh();
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	Bind(wxEVT_CONTEXT_MENU, [=](wxContextMenuEvent &ev){
 | 
			
		||||
		wxPoint position = ScreenToClient(wxGetMousePosition());
 | 
			
		||||
		
 | 
			
		||||
		size_t kfsIdx, kfIdx;
 | 
			
		||||
		if(MouseOverKF(ScreenToClient(wxGetMousePosition()), kfsIdx, kfIdx)) {
 | 
			
		||||
			wxMenu menu;
 | 
			
		||||
			
 | 
			
		||||
			int idDel = menu.Append(wxID_ANY, "Delete")->GetId();
 | 
			
		||||
			
 | 
			
		||||
			menu.Bind(wxEVT_MENU, [=](wxCommandEvent &ev){
 | 
			
		||||
				if(ev.GetId() == idDel) {
 | 
			
		||||
					auto f = (Frame*) GetParent();
 | 
			
		||||
					auto kfs = f->graph->backendNG->keyframesList.keyframes[kfsIdx];
 | 
			
		||||
					
 | 
			
		||||
					CHi_DeleteKeyframe(f->graph->backendNG, kfs, kfIdx);
 | 
			
		||||
					
 | 
			
		||||
					Refresh();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			
 | 
			
		||||
			PopupMenu(&menu);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	
 | 
			
		||||
	SetMinSize(wxSize{0, 64});
 | 
			
		||||
	//SetSize(wxSize{GetSize().x, std::max(GetMinSize().y, GetSize().y)});
 | 
			
		||||
	Fit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Timeline::Paint(wxPaintEvent &ev) {
 | 
			
		||||
	auto frame = (Frame*) GetParent();
 | 
			
		||||
	
 | 
			
		||||
	wxPaintDC dc{this};
 | 
			
		||||
	
 | 
			
		||||
	dc.SetPen(wxPen{wxColour{160, 60, 60}});
 | 
			
		||||
	
 | 
			
		||||
	{
 | 
			
		||||
		int x = CHi_Time_Get(frame->graph->backendNG) * scale - camX + ZERO_TIME_BASE;
 | 
			
		||||
		dc.DrawLine(x, 0, x, GetSize().y);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	dc.SetPen(wxPen{wxSystemSettings::GetColour(wxSYS_COLOUR_INACTIVECAPTIONTEXT)});
 | 
			
		||||
	
 | 
			
		||||
	float t = std::ceil((float) camX / scale);
 | 
			
		||||
	for(int64_t x = ZERO_TIME_BASE + mod<int64_t>(-camX, scale); x < GetSize().x; x += scale) {
 | 
			
		||||
		dc.DrawLine(x, 0, x, 10);
 | 
			
		||||
		dc.DrawText(wxString::Format("%gs", t), x + 4, 0);
 | 
			
		||||
		
 | 
			
		||||
		t++;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	auto kfsList = &frame->graph->backendNG->keyframesList;
 | 
			
		||||
	
 | 
			
		||||
	for(size_t kfsIdx = 0; kfsIdx < kfsList->count; kfsIdx++) {
 | 
			
		||||
		
 | 
			
		||||
		CHiKeyframes *kfs = kfsList->keyframes[kfsIdx];
 | 
			
		||||
		
 | 
			
		||||
		for(size_t kfIdx = 0; kfIdx < kfs->count; kfIdx++) {
 | 
			
		||||
			wxBitmap &bmp = kfIdx == kfs->count - 1 && kfs->extrapolationMode != CUTIHI_EXTRAPOLATION_NONE ? bmpKfExtrap : bmpKf;
 | 
			
		||||
			
 | 
			
		||||
			dc.DrawBitmap(bmp, ZERO_TIME_BASE - camX + scale * kfs->times[kfIdx] - bmpKf.GetWidth() / 2, bmpKf.GetHeight() * (kfsIdx + 1));
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								ui/timeline.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								ui/timeline.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
#ifndef _CUTICLE_TIMELINE_H
 | 
			
		||||
#define _CUTICLE_TIMELINE_H
 | 
			
		||||
 | 
			
		||||
#include<wx/frame.h>
 | 
			
		||||
#include<wx/panel.h>
 | 
			
		||||
 | 
			
		||||
struct Frame;
 | 
			
		||||
 | 
			
		||||
struct Timeline : wxPanel {
 | 
			
		||||
	int64_t camX = 0;
 | 
			
		||||
	
 | 
			
		||||
	int64_t mouseX = 0;
 | 
			
		||||
	
 | 
			
		||||
	size_t captureKfsIdx, captureKfIdx;
 | 
			
		||||
	
 | 
			
		||||
	enum class CaptureMode {
 | 
			
		||||
		CAM, KF
 | 
			
		||||
	} captureMode;
 | 
			
		||||
	
 | 
			
		||||
	int scale = 100;
 | 
			
		||||
	
 | 
			
		||||
	Timeline(struct Frame *parent);
 | 
			
		||||
	virtual ~Timeline() = default;
 | 
			
		||||
	
 | 
			
		||||
	void Paint(wxPaintEvent&);
 | 
			
		||||
	
 | 
			
		||||
	bool MouseOverKF(wxPoint p, size_t &kfsIdx, size_t &kfIdx);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user