Add Save/Load functionality

This commit is contained in:
mid 2024-06-30 14:43:13 +03:00
parent 34e7cd9362
commit 8f053bbdb1
17 changed files with 1454 additions and 204 deletions

View File

@ -3,9 +3,9 @@ CXXFLAGS := -D_POSIX_C_SOURCE=200809L -Wno-narrowing -march=native -flto -Wall -
LDFLAGS := -lwebm -lpng -lvpx -lsail -lsail-manip `pkg-config --libs pango opus libv4l2` -lportaudio -lXtst
ifneq ($(RELEASE),0)
CXXFLAGS := $(CXXFLAGS) -O0 -g3 -fsanitize=address
CXXFLAGS := $(CXXFLAGS) -O0 -g3 -fsanitize=address -DMTR_ENABLED
else
CXXFLAGS := $(CXXFLAGS) -O3 -fopenmp
CXXFLAGS := $(CXXFLAGS) -O3 -fopenmp -DMTR_ENABLED
endif
all:
@ -19,6 +19,7 @@ all:
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)
gcc $(CXXFLAGS) -std=c99 -shared -c -o minitrace.o hi/minitrace.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 minitrace.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`

View File

@ -7,7 +7,15 @@ Features:
* WebM decoding (VP8, VP9 video; Opus audio)
* WebM encoding (VP8, VP9 video; Opus audio)
* 16-bit linear RGB processing
* Fixed 48kHz internal sample rate, 30Hz frame rate
* Powerful editing capability brought by node graphs
* Node keyframing
Development-wise, the software currently needs a large amount of refactoring, but it's mostly capable for basic recording.
Todo:
* Masking polygons
* Configuable framerate
* More container & codec support
* Use vaapi for accelerating video processing

BIN
btn_load.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
btn_save.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -9,6 +9,8 @@
#include<string.h>
#include"microphone.h"
#include"minitrace.h"
#define pabufsize (48000*5)
typedef struct CHiMicrophoneNode {
@ -48,11 +50,26 @@ static int pacallback(const void *input_, void *output, unsigned long samples, c
static int microphone_start(CHiPubNode *pubn) {
CHiMicrophoneNode *node = (void*) pubn;
int dev;
for(dev = 0; dev < CHi_Microphone_GetSourceCount(); dev++) {
if(!strcmp(CHi_Microphone_GetSourceName(dev), CHi_Crawl(&pubn->sinks[0])->data.text)) {
break;
}
}
if(dev == CHi_Microphone_GetSourceCount()) {
for(dev = 0; dev < CHi_Microphone_GetSourceCount(); dev++) {
if(!strcmp(CHi_Microphone_GetSourceName(dev), "default")) {
break;
}
}
}
PaStreamParameters params = {
.device = pubn->sinks[0].data.vec4[0],
.device = dev,
.channelCount = 1,
.sampleFormat = paInt16,
.suggestedLatency = Pa_GetDeviceInfo(pubn->sinks[0].data.vec4[0])->defaultLowInputLatency,
.suggestedLatency = Pa_GetDeviceInfo(dev)->defaultLowInputLatency,
};
Pa_OpenStream(&node->paStream, &params, NULL, 48000, 0, paNoFlag, pacallback, pubn);
Pa_StartStream(node->paStream);
@ -72,6 +89,8 @@ static int microphone_stop(CHiPubNode *pubn) {
static int microphone_perform(CHiPubNode *pubn) {
CHiMicrophoneNode *node = (void*) pubn;
MTR_BEGIN("CHi", "microphone_perform");
if(pubn->sources[0].data.sample) {
CHi_Image_Free(pubn->sources[0].data.sample);
}
@ -94,6 +113,8 @@ static int microphone_perform(CHiPubNode *pubn) {
pubn->clean = 0;
MTR_END("CHi", "microphone_perform");
return 1;
}
@ -178,7 +199,7 @@ CUTIVIS int CHi_ExportWav_Stop(CHiPubNode *pubn) {
return 1;
}
CUTIVIS CHiPubNode *CHi_ExportWav() {
struct CHiExportWavNode *n = malloc(sizeof(*n));
struct CHiExportWavNode *n = calloc(1, sizeof(*n));
n->pubn.type = CUTIHI_T('CExp','Wave');
n->pubn.Start = CHi_ExportWav_Start;
n->pubn.Perform = exportwav_perform;

487
hi/minitrace.c Normal file
View File

@ -0,0 +1,487 @@
// minitrace
// Copyright 2014 by Henrik Rydgård
// http://www.github.com/hrydgard/minitrace
// Released under the MIT license.
// See minitrace.h for basic documentation.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#pragma warning (disable:4996)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define __thread __declspec(thread)
#define pthread_mutex_t CRITICAL_SECTION
#define pthread_mutex_init(a, b) InitializeCriticalSection(a)
#define pthread_mutex_lock(a) EnterCriticalSection(a)
#define pthread_mutex_unlock(a) LeaveCriticalSection(a)
#define pthread_mutex_destroy(a) DeleteCriticalSection(a)
#else
#include <signal.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#include "minitrace.h"
#ifdef __GNUC__
#define ATTR_NORETURN __attribute__((noreturn))
#else
#define ATTR_NORETURN
#endif
#define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0])
#define TRUE 1
#define FALSE 0
// Ugh, this struct is already pretty heavy.
// Will probably need to move arguments to a second buffer to support more than one.
typedef struct raw_event {
const char *name;
const char *cat;
void *id;
int64_t ts;
uint32_t pid;
uint32_t tid;
char ph;
mtr_arg_type arg_type;
const char *arg_name;
union {
const char *a_str;
int a_int;
double a_double;
};
} raw_event_t;
static raw_event_t *event_buffer;
static raw_event_t *flush_buffer;
static volatile int event_count;
static int is_tracing = FALSE;
static int is_flushing = FALSE;
static int events_in_progress = 0;
static int64_t time_offset;
static int first_line = 1;
static FILE *f;
static __thread int cur_thread_id; // Thread local storage
static int cur_process_id;
static pthread_mutex_t mutex;
static pthread_mutex_t event_mutex;
#define STRING_POOL_SIZE 100
static char *str_pool[100];
// forward declaration
void mtr_flush_with_state(int);
// Tiny portability layer.
// Exposes:
// get_cur_thread_id()
// get_cur_process_id()
// mtr_time_s()
// pthread basics
#ifdef _WIN32
static int get_cur_thread_id() {
return (int)GetCurrentThreadId();
}
static int get_cur_process_id() {
return (int)GetCurrentProcessId();
}
static uint64_t _frequency = 0;
static uint64_t _starttime = 0;
double mtr_time_s() {
if (_frequency == 0) {
QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency);
QueryPerformanceCounter((LARGE_INTEGER*)&_starttime);
}
__int64 time;
QueryPerformanceCounter((LARGE_INTEGER*)&time);
return ((double) (time - _starttime) / (double) _frequency);
}
// Ctrl+C handling for Windows console apps
static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
if (is_tracing && fdwCtrlType == CTRL_C_EVENT) {
printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
mtr_flush();
mtr_shutdown();
}
ExitProcess(1);
}
void mtr_register_sigint_handler() {
// For console apps:
SetConsoleCtrlHandler(&CtrlHandler, TRUE);
}
#else
static inline int get_cur_thread_id() {
return (int)(intptr_t)pthread_self();
}
static inline int get_cur_process_id() {
return (int)getpid();
}
#if defined(BLACKBERRY)
double mtr_time_s() {
struct timespec time;
clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps
return time.tv_sec + time.tv_nsec / 1.0e9;
}
#else
double mtr_time_s() {
static time_t start;
struct timeval tv;
gettimeofday(&tv, NULL);
if (start == 0) {
start = tv.tv_sec;
}
tv.tv_sec -= start;
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
}
#endif // !BLACKBERRY
static void termination_handler(int signum) ATTR_NORETURN;
static void termination_handler(int signum) {
(void) signum;
if (is_tracing) {
printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
mtr_flush();
fwrite("\n]}\n", 1, 4, f);
fclose(f);
}
exit(1);
}
void mtr_register_sigint_handler() {
#ifndef MTR_ENABLED
return;
#endif
// Avoid altering set-to-be-ignored handlers while registering.
if (signal(SIGINT, &termination_handler) == SIG_IGN)
signal(SIGINT, SIG_IGN);
}
#endif
void mtr_init_from_stream(void *stream) {
#ifndef MTR_ENABLED
return;
#endif
event_buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
flush_buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
is_flushing = FALSE;
is_tracing = 1;
event_count = 0;
f = (FILE *)stream;
const char *header = "{\"traceEvents\":[\n";
fwrite(header, 1, strlen(header), f);
time_offset = (uint64_t)(mtr_time_s() * 1000000);
first_line = 1;
pthread_mutex_init(&mutex, 0);
pthread_mutex_init(&event_mutex, 0);
}
void mtr_init(const char *json_file) {
#ifndef MTR_ENABLED
return;
#endif
mtr_init_from_stream(fopen(json_file, "wb"));
}
void mtr_shutdown() {
int i;
#ifndef MTR_ENABLED
return;
#endif
pthread_mutex_lock(&mutex);
is_tracing = FALSE;
pthread_mutex_unlock(&mutex);
mtr_flush_with_state(TRUE);
fwrite("\n]}\n", 1, 4, f);
fclose(f);
pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&event_mutex);
f = 0;
free(event_buffer);
event_buffer = 0;
free(flush_buffer);
flush_buffer = 0;
for (i = 0; i < STRING_POOL_SIZE; i++) {
if (str_pool[i]) {
free(str_pool[i]);
str_pool[i] = 0;
}
}
}
const char *mtr_pool_string(const char *str) {
int i;
for (i = 0; i < STRING_POOL_SIZE; i++) {
if (!str_pool[i]) {
str_pool[i] = (char*)malloc(strlen(str) + 1);
strcpy(str_pool[i], str);
return str_pool[i];
} else {
if (!strcmp(str, str_pool[i]))
return str_pool[i];
}
}
return "string pool full";
}
void mtr_start() {
#ifndef MTR_ENABLED
return;
#endif
pthread_mutex_lock(&mutex);
is_tracing = TRUE;
pthread_mutex_unlock(&mutex);
}
void mtr_stop() {
#ifndef MTR_ENABLED
return;
#endif
pthread_mutex_lock(&mutex);
is_tracing = FALSE;
pthread_mutex_unlock(&mutex);
}
// TODO: fwrite more than one line at a time.
// Flushing is thread safe and process async
// using double-buffering mechanism.
// Aware: only one flushing process may be
// running at any point of time
void mtr_flush_with_state(int is_last) {
#ifndef MTR_ENABLED
return;
#endif
int i = 0;
char linebuf[1024];
char arg_buf[1024];
char id_buf[256];
int event_count_copy = 0;
int events_in_progress_copy = 1;
raw_event_t *event_buffer_tmp = NULL;
// small critical section to swap buffers
// - no any new events can be spawn while
// swapping since they tied to the same mutex
// - checks for any flushing in process
pthread_mutex_lock(&mutex);
// if not flushing already
if (is_flushing) {
pthread_mutex_unlock(&mutex);
return;
}
is_flushing = TRUE;
event_count_copy = event_count;
event_buffer_tmp = flush_buffer;
flush_buffer = event_buffer;
event_buffer = event_buffer_tmp;
event_count = 0;
// waiting for any unfinished events before swap
while (events_in_progress_copy != 0) {
pthread_mutex_lock(&event_mutex);
events_in_progress_copy = events_in_progress;
pthread_mutex_unlock(&event_mutex);
}
pthread_mutex_unlock(&mutex);
for (i = 0; i < event_count_copy; i++) {
raw_event_t *raw = &flush_buffer[i];
int len;
switch (raw->arg_type) {
case MTR_ARG_TYPE_INT:
snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int);
break;
case MTR_ARG_TYPE_STRING_CONST:
snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
break;
case MTR_ARG_TYPE_STRING_COPY:
if (strlen(raw->a_str) > 700) {
snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%.*s\"", raw->arg_name, 700, raw->a_str);
} else {
snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
}
break;
case MTR_ARG_TYPE_NONE:
arg_buf[0] = '\0';
break;
}
if (raw->id) {
switch (raw->ph) {
case 'S':
case 'T':
case 'F':
// TODO: Support full 64-bit pointers
snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id);
break;
case 'X':
snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double);
break;
}
} else {
id_buf[0] = 0;
}
const char *cat = raw->cat;
#ifdef _WIN32
// On Windows, we often end up with backslashes in category.
char temp[256];
{
int len = (int)strlen(cat);
int i;
if (len > 255) len = 255;
for (i = 0; i < len; i++) {
temp[i] = cat[i] == '\\' ? '/' : cat[i];
}
temp[len] = 0;
cat = temp;
}
#endif
len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}",
first_line ? "" : ",\n",
cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf);
fwrite(linebuf, 1, len, f);
first_line = 0;
if (raw->arg_type == MTR_ARG_TYPE_STRING_COPY) {
free((void*)raw->a_str);
}
#ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
free(raw->name);
free(raw->cat);
#endif
}
pthread_mutex_lock(&mutex);
is_flushing = is_last;
pthread_mutex_unlock(&mutex);
}
void mtr_flush() {
mtr_flush_with_state(FALSE);
}
void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) {
#ifndef MTR_ENABLED
return;
#endif
pthread_mutex_lock(&mutex);
if (!is_tracing || event_count >= INTERNAL_MINITRACE_BUFFER_SIZE) {
pthread_mutex_unlock(&mutex);
return;
}
raw_event_t *ev = &event_buffer[event_count];
++event_count;
pthread_mutex_lock(&event_mutex);
++events_in_progress;
pthread_mutex_unlock(&event_mutex);
pthread_mutex_unlock(&mutex);
double ts = mtr_time_s();
if (!cur_thread_id) {
cur_thread_id = get_cur_thread_id();
}
if (!cur_process_id) {
cur_process_id = get_cur_process_id();
}
#ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
const size_t category_len = strlen(category);
ev->cat = malloc(category_len + 1);
strcpy(ev->cat, category);
const size_t name_len = strlen(name);
ev->name = malloc(name_len + 1);
strcpy(ev->name, name);
#else
ev->cat = category;
ev->name = name;
#endif
ev->id = id;
ev->ph = ph;
if (ev->ph == 'X') {
double x;
memcpy(&x, id, sizeof(double));
ev->ts = (int64_t)(x * 1000000);
ev->a_double = (ts - x) * 1000000;
} else {
ev->ts = (int64_t)(ts * 1000000);
}
ev->tid = cur_thread_id;
ev->pid = cur_process_id;
ev->arg_type = MTR_ARG_TYPE_NONE;
pthread_mutex_lock(&event_mutex);
--events_in_progress;
pthread_mutex_unlock(&event_mutex);
}
void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value) {
#ifndef MTR_ENABLED
return;
#endif
pthread_mutex_lock(&mutex);
if (!is_tracing || event_count >= INTERNAL_MINITRACE_BUFFER_SIZE) {
pthread_mutex_unlock(&mutex);
return;
}
raw_event_t *ev = &event_buffer[event_count];
++event_count;
pthread_mutex_lock(&event_mutex);
++events_in_progress;
pthread_mutex_unlock(&event_mutex);
pthread_mutex_unlock(&mutex);
if (!cur_thread_id) {
cur_thread_id = get_cur_thread_id();
}
if (!cur_process_id) {
cur_process_id = get_cur_process_id();
}
double ts = mtr_time_s();
#ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
const size_t category_len = strlen(category);
ev->cat = malloc(category_len + 1);
strcpy(ev->cat, category);
const size_t name_len = strlen(name);
ev->name = malloc(name_len + 1);
strcpy(ev->name, name);
#else
ev->cat = category;
ev->name = name;
#endif
ev->id = id;
ev->ts = (int64_t)(ts * 1000000);
ev->ph = ph;
ev->tid = cur_thread_id;
ev->pid = cur_process_id;
ev->arg_type = arg_type;
ev->arg_name = arg_name;
switch (arg_type) {
case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break;
case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break;
case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break;
case MTR_ARG_TYPE_NONE: break;
}
pthread_mutex_lock(&event_mutex);
--events_in_progress;
pthread_mutex_unlock(&event_mutex);
}

276
hi/minitrace.h Normal file
View File

@ -0,0 +1,276 @@
// Minitrace
//
// Copyright 2014 by Henrik Rydgård
// http://www.github.com/hrydgard/minitrace
// Released under the MIT license.
//
// Ultra-light dependency free library for performance tracing C/C++ applications.
// Produces traces compatible with Google Chrome's trace viewer.
// Simply open "about:tracing" in Chrome and load the produced JSON.
//
// This contains far less template magic than the original libraries from Chrome
// because this is meant to be usable from C.
//
// See README.md for a tutorial.
//
// The trace format is documented here:
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
// More:
// http://www.altdevblogaday.com/2012/08/21/using-chrometracing-to-view-your-inline-profiling-data/
#ifndef MINITRACE_H
#define MINITRACE_H
#include <inttypes.h>
#ifdef MTR_BUILDING_WITH_CMAKE
#include "minitrace_export.h"
#else
#define MINITRACE_EXPORT
#endif
// If MTR_ENABLED is not defined, Minitrace does nothing and has near zero overhead.
// Preferably, set this flag in your build system. If you can't just uncomment this line.
// #define MTR_ENABLED
// By default, will collect up to 1000000 events, then you must flush.
// It's recommended that you simply call mtr_flush on a background thread
// occasionally. It's safe...ish.
#define INTERNAL_MINITRACE_BUFFER_SIZE 1000000
#ifdef __cplusplus
extern "C" {
#endif
// Initializes Minitrace. Must be called very early during startup of your executable,
// before any MTR_ statements.
MINITRACE_EXPORT void mtr_init(const char *json_file);
// Same as above, but allows passing in a custom stream (FILE *), as returned by
// fopen(). It should be opened for writing, preferably in binary mode to avoid
// processing of line endings (i.e. the "wb" mode).
MINITRACE_EXPORT void mtr_init_from_stream(void *stream);
// Shuts down minitrace cleanly, flushing the trace buffer.
MINITRACE_EXPORT void mtr_shutdown(void);
// Lets you enable and disable Minitrace at runtime.
// May cause strange discontinuities in the output.
// Minitrace is enabled on startup by default.
MINITRACE_EXPORT void mtr_start(void);
MINITRACE_EXPORT void mtr_stop(void);
// Flushes the collected data to disk, clearing the buffer for new data.
MINITRACE_EXPORT void mtr_flush(void);
// Returns the current time in seconds. Used internally by Minitrace. No caching.
MINITRACE_EXPORT double mtr_time_s(void);
// Registers a handler that will flush the trace on Ctrl+C.
// Works on Linux and MacOSX, and in Win32 console applications.
MINITRACE_EXPORT void mtr_register_sigint_handler(void);
// Utility function that should rarely be used.
// If str is semi dynamic, store it permanently in a small pool so we don't need to malloc it.
// The pool fills up fast though and performance isn't great.
// Returns a fixed string if the pool is full.
MINITRACE_EXPORT const char *mtr_pool_string(const char *str);
// Commented-out types will be supported in the future.
typedef enum {
MTR_ARG_TYPE_NONE = 0,
MTR_ARG_TYPE_INT = 1, // I
// MTR_ARG_TYPE_FLOAT = 2, // TODO
// MTR_ARG_TYPE_DOUBLE = 3, // TODO
MTR_ARG_TYPE_STRING_CONST = 8, // C
MTR_ARG_TYPE_STRING_COPY = 9,
// MTR_ARG_TYPE_JSON_COPY = 10,
} mtr_arg_type;
// TODO: Add support for more than one argument (metadata) per event
// Having more costs speed and memory.
#define MTR_MAX_ARGS 1
// Only use the macros to call these.
MINITRACE_EXPORT void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id);
MINITRACE_EXPORT void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value);
#ifdef MTR_ENABLED
// c - category. Can be filtered by in trace viewer (or at least that's the intention).
// A good use is to pass __FILE__, there are macros further below that will do it for you.
// n - name. Pass __FUNCTION__ in most cases, unless you are marking up parts of one.
// Scopes. In C++, use MTR_SCOPE. In C, always match them within the same scope.
#define MTR_BEGIN(c, n) internal_mtr_raw_event(c, n, 'B', 0)
#define MTR_END(c, n) internal_mtr_raw_event(c, n, 'E', 0)
#define MTR_SCOPE(c, n) MTRScopedTrace ____mtr_scope(c, n)
#define MTR_SCOPE_LIMIT(c, n, l) MTRScopedTraceLimit ____mtr_scope(c, n, l)
// Async events. Can span threads. ID identifies which events to connect in the view.
#define MTR_START(c, n, id) internal_mtr_raw_event(c, n, 'S', (void *)(id))
#define MTR_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 'T', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
#define MTR_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'F', (void *)(id))
// Flow events. Like async events, but displayed in a more fancy way in the viewer.
#define MTR_FLOW_START(c, n, id) internal_mtr_raw_event(c, n, 's', (void *)(id))
#define MTR_FLOW_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 't', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
#define MTR_FLOW_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'f', (void *)(id))
// The same macros, but with a single named argument which shows up as metadata in the viewer.
// _I for int.
// _C is for a const string arg.
// _S will copy the string, freeing on flush (expensive but sometimes necessary).
// but required if the string was generated dynamically.
// Note that it's fine to match BEGIN_S with END and BEGIN with END_S, etc.
#define MTR_BEGIN_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
#define MTR_END_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
#define MTR_SCOPE_C(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
#define MTR_BEGIN_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
#define MTR_END_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
#define MTR_SCOPE_S(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
#define MTR_BEGIN_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
#define MTR_END_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
#define MTR_SCOPE_I(c, n, aname, aintval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
// Instant events. For things with no duration.
#define MTR_INSTANT(c, n) internal_mtr_raw_event(c, n, 'I', 0)
#define MTR_INSTANT_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
#define MTR_INSTANT_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_INT, aname, (void *)(aintval))
// Counters (can't do multi-value counters yet)
#define MTR_COUNTER(c, n, val) internal_mtr_raw_event_arg(c, n, 'C', 0, MTR_ARG_TYPE_INT, n, (void *)(intptr_t)(val))
// Metadata. Call at the start preferably. Must be const strings.
#define MTR_META_PROCESS_NAME(n) internal_mtr_raw_event_arg("", "process_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
#define MTR_META_THREAD_NAME(n) internal_mtr_raw_event_arg("", "thread_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
#define MTR_META_THREAD_SORT_INDEX(i) internal_mtr_raw_event_arg("", "thread_sort_index", 'M', 0, MTR_ARG_TYPE_INT, "sort_index", (void *)(i))
#else
#define MTR_BEGIN(c, n)
#define MTR_END(c, n)
#define MTR_SCOPE(c, n)
#define MTR_START(c, n, id)
#define MTR_STEP(c, n, id, step)
#define MTR_FINISH(c, n, id)
#define MTR_FLOW_START(c, n, id)
#define MTR_FLOW_STEP(c, n, id, step)
#define MTR_FLOW_FINISH(c, n, id)
#define MTR_INSTANT(c, n)
#define MTR_BEGIN_C(c, n, aname, astrval)
#define MTR_END_C(c, n, aname, astrval)
#define MTR_SCOPE_C(c, n, aname, astrval)
#define MTR_BEGIN_S(c, n, aname, astrval)
#define MTR_END_S(c, n, aname, astrval)
#define MTR_SCOPE_S(c, n, aname, astrval)
#define MTR_BEGIN_I(c, n, aname, aintval)
#define MTR_END_I(c, n, aname, aintval)
#define MTR_SCOPE_I(c, n, aname, aintval)
#define MTR_INSTANT(c, n)
#define MTR_INSTANT_C(c, n, aname, astrval)
#define MTR_INSTANT_I(c, n, aname, aintval)
// Counters (can't do multi-value counters yet)
#define MTR_COUNTER(c, n, val)
// Metadata. Call at the start preferably. Must be const strings.
#define MTR_META_PROCESS_NAME(n)
#define MTR_META_THREAD_NAME(n)
#define MTR_META_THREAD_SORT_INDEX(i)
#endif
// Shortcuts for simple function timing with automatic categories and names.
#define MTR_BEGIN_FUNC() MTR_BEGIN(__FILE__, __FUNCTION__)
#define MTR_END_FUNC() MTR_END(__FILE__, __FUNCTION__)
#define MTR_SCOPE_FUNC() MTR_SCOPE(__FILE__, __FUNCTION__)
#define MTR_INSTANT_FUNC() MTR_INSTANT(__FILE__, __FUNCTION__)
#define MTR_SCOPE_FUNC_LIMIT_S(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, l)
#define MTR_SCOPE_FUNC_LIMIT_MS(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, (double)l * 0.000001)
// Same, but with a single argument of the usual types.
#define MTR_BEGIN_FUNC_S(aname, arg) MTR_BEGIN_S(__FILE__, __FUNCTION__, aname, arg)
#define MTR_END_FUNC_S(aname, arg) MTR_END_S(__FILE__, __FUNCTION__, aname, arg)
#define MTR_SCOPE_FUNC_S(aname, arg) MTR_SCOPE_S(__FILE__, __FUNCTION__, aname, arg)
#define MTR_BEGIN_FUNC_C(aname, arg) MTR_BEGIN_C(__FILE__, __FUNCTION__, aname, arg)
#define MTR_END_FUNC_C(aname, arg) MTR_END_C(__FILE__, __FUNCTION__, aname, arg)
#define MTR_SCOPE_FUNC_C(aname, arg) MTR_SCOPE_C(__FILE__, __FUNCTION__, aname, arg)
#define MTR_BEGIN_FUNC_I(aname, arg) MTR_BEGIN_I(__FILE__, __FUNCTION__, aname, arg)
#define MTR_END_FUNC_I(aname, arg) MTR_END_I(__FILE__, __FUNCTION__, aname, arg)
#define MTR_SCOPE_FUNC_I(aname, arg) MTR_SCOPE_I(__FILE__, __FUNCTION__, aname, arg)
#ifdef __cplusplus
}
#ifdef MTR_ENABLED
// These are optimized to use X events (combined B and E). Much easier to do in C++ than in C.
class MTRScopedTrace {
public:
MTRScopedTrace(const char *category, const char *name)
: category_(category), name_(name) {
start_time_ = mtr_time_s();
}
~MTRScopedTrace() {
internal_mtr_raw_event(category_, name_, 'X', &start_time_);
}
private:
const char *category_;
const char *name_;
double start_time_;
};
// Only outputs a block if execution time exceeded the limit.
// TODO: This will effectively call mtr_time_s twice at the end, which is bad.
class MTRScopedTraceLimit {
public:
MTRScopedTraceLimit(const char *category, const char *name, double limit_s)
: category_(category), name_(name), limit_(limit_s) {
start_time_ = mtr_time_s();
}
~MTRScopedTraceLimit() {
double end_time = mtr_time_s();
if (end_time - start_time_ >= limit_) {
internal_mtr_raw_event(category_, name_, 'X', &start_time_);
}
}
private:
const char *category_;
const char *name_;
double start_time_;
double limit_;
};
class MTRScopedTraceArg {
public:
MTRScopedTraceArg(const char *category, const char *name, mtr_arg_type arg_type, const char *arg_name, void *arg_value)
: category_(category), name_(name) {
internal_mtr_raw_event_arg(category, name, 'B', 0, arg_type, arg_name, arg_value);
}
~MTRScopedTraceArg() {
internal_mtr_raw_event(category_, name_, 'E', 0);
}
private:
const char *category_;
const char *name_;
};
#endif
#endif
#endif

317
hi/node.c
View File

@ -15,6 +15,7 @@
#include<math.h>
#include<sched.h>
#include<limits.h>
#include"minitrace.h"
#include"linearity.h"
@ -69,18 +70,60 @@ static void adjacency_remove(CHiPubNode *source, CHiPubNode *sink) {
}
CUTIVIS CHiNodeGraph *CHi_NewNodeGraph() {
static int inited = 0;
if(!inited) {
inited = 1;
mtr_init("CHiTrace.json");
}
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);
CHi_NodeGraphReset(ret);
return ret;
}
CUTIVIS CHiNodeGraph *CHi_NodeGraphReset(CHiNodeGraph *ng) {
for(size_t n = 0; n < ng->count; n++) {
if(ng->nodes[n]->Destroy) {
ng->nodes[n]->Destroy(ng->nodes[n]);
} else {
free(ng->nodes[n]);
}
}
if(ng->nodes) {
free(ng->nodes);
}
if(ng->adjacencies) {
free(ng->adjacencies);
}
if(ng->keyframesList.keyframes) {
free(ng->keyframesList.keyframes);
}
void *eOnStop = ng->eventOnStopComplete;
void *eOnFrame = ng->eventOnFrameComplete;
void *ud = ng->ud;
memset(ng, 0, sizeof(*ng));
ng->count = 0;
ng->nodes = malloc(sizeof(*ng->nodes) * (ng->capacity = 8));
ng->eventOnStopComplete = NULL;
ng->eventOnFrameComplete = NULL;
ng->compilationStatus = CUTIHI_COMP_READY;
ng->adjacencyCount = 0;
ng->adjacencyCapacity = 8;
ng->adjacencies = malloc(sizeof(CHiAdjacency) * ng->adjacencyCapacity);
ng->eventOnStopComplete = eOnStop;
ng->eventOnFrameComplete = eOnFrame;
ng->ud = ud;
return ng;
}
CUTIVIS CHiValue *CHi_Crawl(CHiValue *v) {
while(v->type == CUTIHI_VAL_LINKED || v->type == CUTIHI_VAL_KEYED) {
if(v->type == CUTIHI_VAL_LINKED) {
@ -434,6 +477,8 @@ void *compile_thread(void *ctx_) {
free(ctx);
mtr_flush();
return NULL;
}
CUTIVIS void CHi_BeginCompilation(CHiNodeGraph *ng) {
@ -487,7 +532,7 @@ err:
return 0;
}
CUTIVIS CHiPubNode *CHi_Image() {
CHiPubNode *n = malloc(sizeof(*n));
CHiPubNode *n = calloc(1, sizeof(*n));
n->type = CUTIHI_T('CIma','ge ');
n->Start = n->Stop = NULL;
n->Perform = image_perform;
@ -502,6 +547,8 @@ CUTIVIS CHiPubNode *CHi_Image() {
static int embed_perform(CHiPubNode *node) {
if(node->clean) return 1;
MTR_BEGIN("CHi", "embed_perform");
node->sources[0].type = CUTIHI_VAL_SAMPLE;
CHiImage *main = CHi_Crawl(&node->sinks[0])->data.sample;
@ -541,11 +588,13 @@ static int embed_perform(CHiPubNode *node) {
}
}
MTR_END("CHi", "embed_perform");
node->clean = 0;
return 1;
}
CUTIVIS CHiPubNode *CHi_Embed() {
CHiPubNode *n = malloc(sizeof(*n));
CHiPubNode *n = calloc(1, sizeof(*n));
n->type = CUTIHI_T('CEmb','ed ');
n->Start = n->Stop = NULL;
n->Perform = embed_perform;
@ -583,7 +632,7 @@ static int constantsample_perform(CHiPubNode *node) {
return 1;
}
CUTIVIS CHiPubNode *CHi_ConstantSample() {
CHiPubNode *n = malloc(sizeof(*n));
CHiPubNode *n = calloc(1, sizeof(*n));
n->type = CUTIHI_T('CCns','tCol');
n->Start = n->Stop = NULL;
n->Perform = constantsample_perform;
@ -598,16 +647,20 @@ CUTIVIS CHiPubNode *CHi_ConstantSample() {
static int modulate_perform(CHiPubNode *node) {
if(node->clean) return 1;
MTR_BEGIN("CHi", "modulate_perform");
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);
MTR_END("CHi", "modulate_perform");
node->clean = 0;
return 1;
}
CUTIVIS CHiPubNode *CHi_Modulate() {
CHiPubNode *n = malloc(sizeof(*n));
CHiPubNode *n = calloc(1, sizeof(*n));
n->type = CUTIHI_T('CMod','ulat');
n->Start = n->Stop = NULL;
n->Perform = modulate_perform;
@ -682,7 +735,7 @@ CUTIVIS float CHi_Time_GetDelta(CHiNodeGraph *ng) {
return ng->timedelta;
}
CUTIVIS CHiPubNode *CHi_Time() {
CHiPubNode *n = malloc(sizeof(*n));
CHiPubNode *n = calloc(1, sizeof(*n));
n->type = CUTIHI_T('CTim','e ');
n->Start = n->Stop = NULL;
n->Perform = time_perform;
@ -701,6 +754,8 @@ static PangoLayout *playout;
static int text_perform(CHiPubNode *n) {
if(n->clean) return 1;
MTR_BEGIN("CHi", "text_perform");
if(!pfontmap) {
pfontmap = pango_ft2_font_map_new();
pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(pfontmap), 72, 72);
@ -750,11 +805,13 @@ static int text_perform(CHiPubNode *n) {
}
free(bmp.buffer);
MTR_END("CHi", "text_perform");
n->clean = 0;
return 1;
}
CUTIVIS CHiPubNode *CHi_Text() {
CHiPubNode *n = malloc(sizeof(*n));
CHiPubNode *n = calloc(1, sizeof(*n));
n->type = CUTIHI_T('CTex','t ');
n->Start = n->Stop = NULL;
n->Perform = text_perform;
@ -769,6 +826,8 @@ CUTIVIS CHiPubNode *CHi_Text() {
static int mixer_perform(CHiPubNode *n) {
n->sources[0].type = CUTIHI_VAL_SAMPLE;
MTR_BEGIN("CHi", "mixer_perform");
if(n->sources[0].data.sample) {
CHi_Image_Free(n->sources[0].data.sample);
n->sources[0].data.sample = NULL;
@ -791,6 +850,8 @@ static int mixer_perform(CHiPubNode *n) {
_mm_stream_si128((__m128i*) ((uintptr_t) n->sources[0].data.sample->data16 + b), _mm_adds_epi16(a0, a1));
}
MTR_END("CHi", "mixer_perform");
n->clean = 0;
return 1;
}
@ -809,7 +870,7 @@ static int preview_perform(CHiPubNode *n) {
return 1;
}
CUTIVIS CHiPubNode *CHi_Preview() {
CHiPubNode *n = malloc(sizeof(*n));
CHiPubNode *n = calloc(1, sizeof(*n));
n->type = CUTIHI_T('CPre','view');
n->Start = n->Stop = NULL;
n->Perform = preview_perform;
@ -839,6 +900,7 @@ static void save_chival(CHiNodeGraph *ng, CHiSaveWriter writer, CHiValType type,
assert(index < ng->count);
writer(ud, &(uint64_t) {index}, sizeof(uint64_t));
writer(ud, &(uint16_t) {data.linked.idx}, sizeof(uint16_t));
} else if(type == CUTIHI_VAL_KEYED) {
size_t index;
for(index = 0; index < ng->keyframesList.count; index++) {
@ -852,30 +914,42 @@ static void save_chival(CHiNodeGraph *ng, CHiSaveWriter writer, CHiValType type,
}
}
static void load_chival(CHiNodeGraph *ng, CHiLoadReader reader, CHiValType type, CHiValueRaw *data, void *ud) {
if(type == CUTIHI_VAL_TEXT) {
uint32_t len;
reader(ud, &len, sizeof(len));
data->text = malloc(len + 1);
reader(ud, data->text, len);
data->text[len] = 0;
} else if(type == CUTIHI_VAL_VEC4) {
reader(ud, data->vec4, sizeof(data->vec4));
} else if(type == CUTIHI_VAL_LINKED) {
uint64_t index;
reader(ud, &index, sizeof(index));
data->linked.to = ng->nodes[index];
uint16_t idx;
reader(ud, &idx, sizeof(idx));
data->linked.idx = idx;
} else if(type == CUTIHI_VAL_KEYED) {
uint64_t index;
reader(ud, &index, sizeof(index));
data->keyed = ng->keyframesList.keyframes[index];
}
}
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++) {
@ -895,5 +969,182 @@ CUTIVIS int CHi_NodeGraphSave(CHiNodeGraph *ng, CHiSaveWriter writer, void *ud)
writer(ud, kfs->extrapolationParameter, sizeof(kfs->extrapolationParameter));
}
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, &(uint64_t) {node->type}, sizeof(uint64_t));
}
for(size_t i = 0; i < ng->count; i++) {
CHiPubNode *node = ng->nodes[i];
if(node->Save) {
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);
}
}
return 0;
}
CUTIVIS int CHi_NodeGraphLoad(CHiNodeGraph *ng, CHiLoadReader reader, void *ud) {
{
char magic[4];
reader(ud, magic, sizeof(magic));
if(memcmp(magic, "\x71\x74\xCE\xA0", 4)) {
return 1;
}
}
CHi_NodeGraphReset(ng);
reader(ud, &ng->duration, sizeof(float));
reader(ud, &ng->time, sizeof(float));
{
uint64_t count;
reader(ud, &count, sizeof(count));
ng->keyframesList.count = count;
}
for(size_t i = 0; i < ng->keyframesList.count; i++) {
CHiKeyframes *kfs = ng->keyframesList.keyframes[i] = calloc(1, sizeof(*kfs));
{
uint16_t type;
reader(ud, &type, sizeof(type));
kfs->type = type;
}
{
uint64_t count;
reader(ud, &count, sizeof(count));
kfs->count = count;
}
kfs->times = calloc(kfs->count, sizeof(*kfs->times));
reader(ud, kfs->times, kfs->count * sizeof(*kfs->times));
for(size_t k = 0; k < kfs->count; k++) {
load_chival(ng, reader, kfs->type, &kfs->values[k], ud);
}
{
uint16_t extrap;
reader(ud, &extrap, sizeof(extrap));
kfs->extrapolationMode = extrap;
}
reader(ud, kfs->extrapolationParameter, sizeof(kfs->extrapolationParameter));
}
{
uint64_t count;
reader(ud, &count, sizeof(count));
ng->count = count;
}
ng->capacity = ng->count < 8 ? 8 : ng->count;
ng->nodes = calloc(ng->capacity, sizeof(*ng->nodes));
for(size_t i = 0; i < ng->count; i++) {
uint64_t type;
reader(ud, &type, sizeof(type));
CHiPubNode *n = NULL;
if(type == CUTIHI_T('CPre','view')) {
n = CHi_Preview();
} else if(type == CUTIHI_T('CMix','er ')) {
n = CHi_Mixer();
} else if(type == CUTIHI_T('CTex','t ')) {
n = CHi_Text();
} else if(type == CUTIHI_T('CTim','e ')) {
n = CHi_Time();
} else if(type == CUTIHI_T('CMod','ulat')) {
n = CHi_Modulate();
} else if(type == CUTIHI_T('CCns','tCol')) {
n = CHi_ConstantSample();
} else if(type == CUTIHI_T('CEmb','ed ')) {
n = CHi_Embed();
} else if(type == CUTIHI_T('CIma','ge ')) {
n = CHi_Image();
} else if(type == CUTIHI_T('CWin','dow ')) {
n = CHi_Window();
} else if(type == CUTIHI_T('CInA','udio')) {
n = CHi_Microphone();
} else if(type == CUTIHI_T('CExp','Wave')) {
n = CHi_ExportWav();
} else if(type == CUTIHI_T('CMov','ie ')) {
n = CHi_Movie();
} else if(type == CUTIHI_T('CEnc','GVP8')) {
n = CHi_EncodeVP8();
} else if(type == CUTIHI_T('CEnc','GVP9')) {
n = CHi_EncodeVP9();
} else if(type == CUTIHI_T('CExp','Webm')) {
n = CHi_MuxWebm();
} else if(type == CUTIHI_T('CKey','hook')) {
n = CHi_Keyhook();
} else if(type == CUTIHI_T('CKey','hook')) {
n = CHi_Keyhook();
} else if(type == CUTIHI_T('CEnc','Opus')) {
n = CHi_EncodeOpus();
} else if(type == CUTIHI_T('CWeb','Cam ')) {
n = CHi_Camera();
} else if(type == CUTIHI_T('CCmp','nScl')) {
n = CHi_ComponentScale();
}
n->ng = ng;
if(!n) {
CHi_NodeGraphReset(ng);
puts("Error: Unknown node type!");
return 1;
}
ng->nodes[i] = n;
}
for(size_t i = 0; i < ng->count; i++) {
CHiPubNode *n = ng->nodes[i];
{
uint16_t u16;
reader(ud, &u16, sizeof(u16));
n->sinkCount = u16;
}
n->sinks = calloc(n->sinkCount, sizeof(*n->sinks));
for(size_t s = 0; s < n->sinkCount; s++) {
{
uint16_t u16;
reader(ud, &u16, sizeof(u16));
n->sinks[s].type = u16;
}
load_chival(ng, reader, n->sinks[s].type, &n->sinks[s].data, ud);
if(n->sinks[s].type == CUTIHI_VAL_LINKED) {
adjacency_add(n->sinks[s].data.linked.to, n);
}
}
}
update_keyed_values(ng);
return 0;
}

View File

@ -17,6 +17,7 @@ extern "C" {
#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 size_t(*CHiLoadReader)(void *ud, void *data, size_t len);
typedef enum {
CUTIHI_VAL_NONE = 0,
@ -68,6 +69,8 @@ typedef struct CHiPubNode {
int (*Stop)(struct CHiPubNode *node);
char clean;
void (*Destroy)(struct CHiPubNode *node);
int (*Save)(struct CHiPubNode *node, void *ud, CHiSaveWriter writer);
size_t sinkCount;
@ -137,7 +140,9 @@ typedef struct CHiNodeGraph {
} CHiNodeGraph;
CUTIVIS CHiNodeGraph *CHi_NewNodeGraph();
CUTIVIS CHiNodeGraph *CHi_NodeGraphReset(CHiNodeGraph *ng);
CUTIVIS int CHi_NodeGraphSave(CHiNodeGraph *ng, CHiSaveWriter writer, void *ud);
CUTIVIS int CHi_NodeGraphLoad(CHiNodeGraph *ng, CHiLoadReader reader, void *ud);
CUTIVIS void CHi_RegisterNode(CHiNodeGraph*, CHiPubNode*);
CUTIVIS void CHi_MakeDirty(CHiNodeGraph*, CHiPubNode*);
@ -156,22 +161,18 @@ 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
@ -179,21 +180,18 @@ CUTIVIS CHiPubNode *CHi_ConstantSample();
#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
@ -204,7 +202,6 @@ 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
@ -212,7 +209,6 @@ 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();
@ -222,39 +218,30 @@ 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*);

View File

@ -53,7 +53,7 @@ static int encodeopus_perform(CHiPubNode *pubn) {
return 1;
}
CUTIVIS CHiPubNode *CHi_EncodeOpus() {
struct CHiEncodeOpusNode *ret = malloc(sizeof(*ret));
struct CHiEncodeOpusNode *ret = calloc(1, sizeof(*ret));
ret->pubn.type = CUTIHI_T('CEnc','Opus');
ret->pubn.Start = CHi_EncodeOpus_Start;
ret->pubn.Perform = &encodeopus_perform;

View File

@ -35,7 +35,7 @@ static int scale_perform(CHiPubNode *n) {
}
CUTIVIS CHiPubNode *CHi_ComponentScale() {
CHiPubNode *n = malloc(sizeof(*n));
CHiPubNode *n = calloc(1, sizeof(*n));
n->type = CUTIHI_T('CCmp','nScl');
n->Start = n->Stop = NULL;
n->Perform = scale_perform;
@ -108,11 +108,21 @@ static int keyhook_perform(CHiPubNode *n) {
return 1;
}
static void keyhook_destroy(CHiPubNode *pubn) {
CHiKeyhookNode *n = (void*) pubn;
XRecordDisableContext(dpy, n->rc);
XRecordFreeContext(dpy, n->rc);
free(n);
}
CUTIVIS CHiPubNode *CHi_Keyhook() {
CHiKeyhookNode *n = malloc(sizeof(*n));
CHiKeyhookNode *n = calloc(1, sizeof(*n));
n->pub.type = CUTIHI_T('CKey','hook');
n->pub.Start = n->pub.Stop = NULL;
n->pub.Perform = keyhook_perform;
n->pub.Destroy = keyhook_destroy;
n->pub.clean = 0;
n->pub.sinkCount = 2;
n->pub.sinks = calloc(sizeof(*n->pub.sinks), n->pub.sinkCount);

View File

@ -125,7 +125,7 @@ CUTIVIS CHiPubNode *CHi_Camera() {
xioctl(camId, VIDIOC_STREAMON, &(enum v4l2_buf_type) {V4L2_BUF_TYPE_VIDEO_CAPTURE});
}
CHiPubNode *pubn = malloc(sizeof(*pubn));
CHiPubNode *pubn = calloc(1, sizeof(*pubn));
pubn->type = CUTIHI_T('CWeb','Cam ');
pubn->clean = 0;
pubn->Start = pubn->Stop = NULL;

View File

@ -21,6 +21,8 @@
#include<math.h>
#include"minitrace.h"
#include"linearity.h"
struct CHiMovieNode;
@ -50,6 +52,12 @@ struct AudioParser final : webm::Callback {
size_t sampleReadI = 0;
int16_t sampleArray[SAMPLE_ARR];
~AudioParser() {
if(opus) {
opus_decoder_destroy(opus);
}
}
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);
@ -272,6 +280,8 @@ webm::Status CueParser::OnCuePoint(const webm::ElementMetadata &metadata, const
static int movie_perform(CHiPubNode *pub) {
CHiMovieNode *node = (CHiMovieNode*) ((uintptr_t) pub - offsetof(CHiMovieNode, pub));
MTR_BEGIN("CHi", "movie_perform");
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;
@ -295,7 +305,7 @@ static int movie_perform(CHiPubNode *pub) {
node->vparser.Feed(&cp, &node->vreader);
free(node->filepathCache);
node->filepathCache = filepath;
node->filepathCache = strdup(filepath);
node->timeCache = std::numeric_limits<int64_t>::max();
if(node->vcodecid == "V_VP9") {
@ -397,15 +407,40 @@ static int movie_perform(CHiPubNode *pub) {
pub->sources[1].data.sample = aud;
pub->clean = 0;
MTR_END("CHi", "movie_perform");
return 1;
}
static void movie_destroy(CHiPubNode *pub) {
CHiMovieNode *node = (CHiMovieNode*) ((uintptr_t) pub - offsetof(CHiMovieNode, pub));
if(node->filepathCache) {
free(node->filepathCache);
}
if(node->af) {
fclose(node->af);
}
if(node->vf) {
fclose(node->vf);
vpx_codec_destroy(&node->codec);
}
node->~CHiMovieNode();
free(node);
}
extern "C" {
CUTIVIS CHiPubNode *CHi_Movie() {
CHiMovieNode *n = (CHiMovieNode*) malloc(sizeof(*n));
CHiMovieNode *n = (CHiMovieNode*) calloc(1, sizeof(*n));
new (n) CHiMovieNode();
n->pub.type = CUTIHI_T('CMov','ie ');
n->pub.Perform = movie_perform;
n->pub.Destroy = movie_destroy;
n->pub.clean = 0;
n->pub.sinkCount = 2;
n->pub.sinks = (CHiValue*) calloc(sizeof(*n->pub.sinks), n->pub.sinkCount);

View File

@ -20,6 +20,8 @@
#include<string.h>
#include"minitrace.h"
#include"linearity.h"
struct CHiEncodeVP9Node {
@ -40,6 +42,8 @@ struct CHiEncodeVP9Node {
static int encodevp9_perform(CHiPubNode *pub) {
CHiEncodeVP9Node *node = (CHiEncodeVP9Node*) ((uintptr_t) pub - offsetof(CHiEncodeVP9Node, pub));
MTR_BEGIN("CHi", "encodevp9_perform");
pub->sources[0].type = CUTIHI_VAL_VP9BS;
pub->sources[0].data.bitstream = NULL;
@ -215,16 +219,24 @@ static int encodevp9_perform(CHiPubNode *pub) {
pub->sources[0].data.bitstream = ret;
MTR_END("CHi", "encodevp9_perform");
return 1;
}
static void encodevp_destroy(CHiPubNode *pub) {
CHiEncodeVP9Node *node = (CHiEncodeVP9Node*) ((uintptr_t) pub - offsetof(CHiEncodeVP9Node, pub));
free(node);
}
CUTIVIS CHiPubNode *CHi_EncodeVP8() {
CHiEncodeVP9Node *n = (CHiEncodeVP9Node*) malloc(sizeof(*n));
CHiEncodeVP9Node *n = (CHiEncodeVP9Node*) calloc(1, 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.Destroy = encodevp_destroy;
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);
@ -234,12 +246,13 @@ CUTIVIS CHiPubNode *CHi_EncodeVP8() {
}
CUTIVIS CHiPubNode *CHi_EncodeVP9() {
CHiEncodeVP9Node *n = (CHiEncodeVP9Node*) malloc(sizeof(*n));
CHiEncodeVP9Node *n = (CHiEncodeVP9Node*) calloc(1, 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.Destroy = encodevp_destroy;
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);
@ -296,6 +309,8 @@ CUTIVIS int CHi_EncodeVP9_Stop(CHiPubNode *pubn) {
_mm_free(node->outU);
_mm_free(node->outV);
vpx_codec_destroy(&node->codec);
return 1;
}
@ -312,6 +327,8 @@ struct CHiMuxWebmNode {
static int muxwebm_perform(CHiPubNode *pubn) {
using namespace mkvmuxer;
MTR_BEGIN("CHi", "muxwebm_perform");
CHiMuxWebmNode *alln = (CHiMuxWebmNode*) pubn;
if(pubn->sinks[1].data.linked.to) {
@ -350,20 +367,29 @@ static int muxwebm_perform(CHiPubNode *pubn) {
alln->videoBacklog.pop();
}
MTR_END("CHi", "muxwebm_perform");
return 1;
}
static void muxwebm_destroy(CHiPubNode *pubn) {
CHiMuxWebmNode *n = (CHiMuxWebmNode*) pubn;
n->~CHiMuxWebmNode();
free(n);
}
CUTIVIS CHiPubNode *CHi_MuxWebm() {
CHiMuxWebmNode *n = (CHiMuxWebmNode*) malloc(sizeof(*n));
CHiMuxWebmNode *n = (CHiMuxWebmNode*) calloc(1, sizeof(*n));
new (n) CHiMuxWebmNode;
n->pub.type = CUTIHI_T('CExp','Webm');
n->pub.Start = CHi_MuxWebm_Start;
n->pub.Perform = muxwebm_perform;
n->pub.Destroy = muxwebm_destroy;
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) {

View File

@ -18,6 +18,8 @@
#include"linearity.h"
#include"minitrace.h"
static Display *d;
static Window root;
@ -69,6 +71,8 @@ typedef struct {
static int window_perform(CHiPubNode *n) {
CHiWindowNode *w = (CHiWindowNode*) n;
MTR_BEGIN("CHi", "window_perform");
Window toshoot;
if(!find_window(d, &toshoot, CHi_Crawl(&n->sinks[0])->data.text)) return 0;
@ -112,26 +116,43 @@ static int window_perform(CHiPubNode *n) {
n->clean = 0;
MTR_END("CHi", "window_perform");
return 1;
}
static void window_destroy(CHiPubNode *pubn) {
CHiWindowNode *n = (void*) pubn;
if(n->vcache) {
XShmDetach(d, &n->shminfo);
shmdt(n->shminfo.shmaddr);
XDestroyImage(n->ximg);
}
free(pubn);
}
CUTIVIS CHiPubNode *CHi_Window() {
if(!d) {
d = XOpenDisplay(NULL);
root = RootWindow(d, DefaultScreen(d));
}
CHiWindowNode *n = malloc(sizeof(*n));
CHiWindowNode *n = calloc(1, sizeof(*n));
n->pub.type = CUTIHI_T('CWin','dow ');
n->pub.Start = n->pub.Stop = NULL;
n->pub.Perform = window_perform;
n->pub.Destroy = window_destroy;
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;
}
@ -147,6 +168,8 @@ CUTIVIS size_t CHi_Window_GetSourceCount() {
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
XFree(list);
return status >= Success ? numItems : 0;
}
@ -163,6 +186,8 @@ CUTIVIS const char *CHi_Window_GetSourceName(size_t idx) {
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
if(status >= Success) {
XFree(list);
status = XGetWMName(d, list[idx], &windowName);
if(status >= Success) {
found = 1;
@ -183,7 +208,9 @@ CUTIVIS uintptr_t CHi_Window_GetSourceData(size_t idx) {
int status = XGetWindowProperty(d, root, atom, 0L, ~0L, 0, AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, (unsigned char**) &list);
if(status >= Success) {
return list[idx];
Window ret = list[idx];
XFree(list);
return ret;
}
return 0;

View File

@ -28,12 +28,97 @@
#include<hi/linearity.h>
static void ShapeGrNode(GrNode *gn) {
if(gn->logical->type == CUTIHI_T('CPre','view')) {
gn->name = "Preview";
gn->sinks = {{"Video", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CMix','er ')) {
gn->name = "Mixer";
gn->sinks = {{"Sink 1", GrNode::Port::Type::SAMPLE}, {"Sink 2", GrNode::Port::Type::SAMPLE}};
gn->sources = {{"Audio", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CTex','t ')) {
gn->name = "Text";
gn->sinks = {{"Text", GrNode::Port::Type::TEXT}, {"Color", GrNode::Port::Type::COLOR}, {"DPI", GrNode::Port::Type::VEC1}};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CTim','e ')) {
gn->name = "Time";
gn->sinks = {};
gn->sources = {{"t", GrNode::Port::Type::VEC1}};
} else if(gn->logical->type == CUTIHI_T('CMod','ulat')) {
gn->name = "Modulate";
gn->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}, {"Brightness", GrNode::Port::Type::VEC1}, {"Contrast", GrNode::Port::Type::VEC1}, {"Hue", GrNode::Port::Type::VEC1}};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CCns','tCol')) {
gn->name = "Constant";
gn->sinks = {{"Color", GrNode::Port::Type::COLOR}};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CEmb','ed ')) {
gn->name = "Embed";
gn->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}
};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CIma','ge ')) {
gn->name = "Image";
gn->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPEN}};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CWin','dow ')) {
gn->name = "Window";
gn->sinks = {{"Name", GrNode::Port::Type::WINDOW_SOURCE}};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CInA','udio')) {
gn->name = "Microphone";
gn->sinks = {{"Source", GrNode::Port::Type::MIC_SOURCE}};
gn->sources = {{"Audio", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CExp','Wave')) {
gn->name = "Mux Wav";
gn->sinks = {{"Filename", GrNode::Port::Type::FILE_SAVE}, {"Audio", GrNode::Port::Type::SAMPLE}};
gn->sources = {};
} else if(gn->logical->type == CUTIHI_T('CMov','ie ')) {
gn->name = "Movie";
gn->sinks = {{"Filepath", GrNode::Port::Type::FILE_OPEN}, {"Time", GrNode::Port::Type::VEC1}};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}, {"Audio", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CEnc','GVP8')) {
gn->name = "Encode VP8";
gn->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}};
gn->sources = {{"Bitstream"}};
} else if(gn->logical->type == CUTIHI_T('CEnc','GVP9')) {
gn->name = "Encode VP9";
gn->sinks = {{"Sample", GrNode::Port::Type::SAMPLE}};
gn->sources = {{"Bitstream"}};
} else if(gn->logical->type == CUTIHI_T('CExp','Webm')) {
gn->name = "Mux WebM";
gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVE}};
gn->sources = {};
} else if(gn->logical->type == CUTIHI_T('CKey','hook')) {
gn->name = "Keyhook";
gn->sinks = {{"Key", GrNode::Port::Type::VEC1}, {"Smooth Time", GrNode::Port::Type::VEC1}};
gn->sources = {{"Bool", GrNode::Port::Type::VEC1}};
} else if(gn->logical->type == CUTIHI_T('CEnc','Opus')) {
gn->name = "Encode Opus";
gn->sinks = {{"Audio", GrNode::Port::Type::SAMPLE}};
gn->sources = {{"Bitstream"}};
} else if(gn->logical->type == CUTIHI_T('CWeb','Cam ')) {
gn->name = "Live Digital Camera";
gn->sinks = {};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
} else if(gn->logical->type == CUTIHI_T('CCmp','nScl')) {
gn->name = "Scale";
gn->sinks = {{"Vector", GrNode::Port::Type::VEC4}, {"Sample", GrNode::Port::Type::SAMPLE}};
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
} else {
puts("Unknown node type.");
}
}
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);
@ -42,9 +127,6 @@ Frame::Frame() : wxFrame(NULL, wxID_ANY, "Cuticle", wxDefaultPosition, {wxSystem
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);
@ -55,15 +137,165 @@ Frame::Frame() : wxFrame(NULL, wxID_ANY, "Cuticle", wxDefaultPosition, {wxSystem
[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();
auto modeTool = tlba->AddTool(wxID_ANY, "Mode", wxBitmap{"tlml.bmp", wxBITMAP_TYPE_ANY});
CHi_SetMode(CUTIHI_MODE_LIVE);
tlba->AddTool(wxID_OPEN, "Load", wxBitmap{"btn_load.bmp", wxBITMAP_TYPE_ANY});
tlba->AddTool(wxID_SAVE, "Save", wxBitmap{"btn_save.bmp", wxBITMAP_TYPE_ANY});
tlba->Bind(wxEVT_COMMAND_TOOL_CLICKED, [=](wxCommandEvent &ev){
if(ev.GetId() == modeTool) {
if(ev.GetId() == modeTool->GetId()) {
CHi_SetMode((CHiMode) ((CHi_GetMode() + 1) % 2));
tlba->SetToolNormalBitmap(modeTool, modeImgs[CHi_GetMode()]);
tlba->SetToolNormalBitmap(modeTool->GetId(), modeImgs[CHi_GetMode()]);
// wxToolBarToolBase::SetShortHelp doesn't work
if(CHi_GetMode() == CUTIHI_MODE_LIVE) {
tlba->SetToolShortHelp(modeTool->GetId(), "Set to live processing.");
} else {
tlba->SetToolShortHelp(modeTool->GetId(), "Set to offline processing.");
}
} else if(ev.GetId() == wxID_SAVE) {
wxFileDialog save{this, "Save", "", "", "Cuticle Project (*.ctc)|*.ctc", wxFD_SAVE | wxFD_OVERWRITE_PROMPT};
if(save.ShowModal() == wxID_OK) {
FILE *f = fopen(save.GetPath().mb_str(), "wb");
CHi_NodeGraphSave(graph->backendNG, +[](void *ud, const void *data, size_t len){
auto f = (FILE*) ud;
return fwrite(data, 1, len, f);
}, f);
for(size_t n = 0; n < graph->backendNG->count; n++) {
GrNode *gn = *std::find_if(graph->gnodes.begin(), graph->gnodes.end(), [=](GrNode *gn){
return gn->logical == graph->backendNG->nodes[n];
});
int32_t pos[2] = {gn->GetPosition().x, gn->GetPosition().y};
fwrite(pos, sizeof(pos), 1, f);
}
fclose(f);
}
} else if(ev.GetId() == wxID_OPEN) {
wxFileDialog load{this, "Load", "", "", "Cuticle Project (*.ctc)|*.ctc", wxFD_OPEN | wxFD_FILE_MUST_EXIST};
if(load.ShowModal() == wxID_OK) {
FILE *f = fopen(load.GetPath().mb_str(), "rb");
if(CHi_NodeGraphLoad(graph->backendNG, +[](void *ud, void *data, size_t len){
auto f = (FILE*) ud;
return fread(data, 1, len, f);
}, f) == 0) {
for(GrNode *gnode : graph->gnodes) {
gnode->Destroy();
}
graph->gnodes.clear();
graph->links.clear();
for(size_t n = 0; n < graph->backendNG->count; n++) {
GrNode *gnode = new GrNode(graph);
int32_t pos[2];
fread(pos, sizeof(pos), 1, f);
gnode->SetPosition({pos[0], pos[1]});
gnode->logical = graph->backendNG->nodes[n];
ShapeGrNode(gnode);
gnode->Fit();
graph->gnodes.push_back(gnode);
}
for(size_t n = 0; n < graph->backendNG->count; n++) {
CHiPubNode *node = graph->backendNG->nodes[n];
for(size_t s = 0; s < node->sinkCount; s++) {
if(node->sinks[s].type == CUTIHI_VAL_LINKED) {
graph->links.push_back(NodeGraph::Link{graph->gnodes[n], s, *std::find_if(graph->gnodes.begin(), graph->gnodes.end(), [=](GrNode *gn){ return gn->logical == node->sinks[s].data.linked.to; }), node->sinks[s].data.linked.idx});
}
}
}
toolbar.duration->SetSeconds(std::max(0.f, graph->backendNG->duration));
if(graph->backendNG->duration <= 0) {
toolbar.durationEnable->SetValue(false);
toolbar.duration->Enable(false);
}
}
fclose(f);
}
}
});
tlba->AddSeparator();
toolbar.durationEnable = new wxCheckBox(tlba, wxID_ANY, "");
toolbar.duration = new ctTimeCtrl(tlba, 120);
toolbar.duration->Bind(wxEVT_COMMAND_TEXT_UPDATED, [=](wxCommandEvent&){
CHi_SetDuration(graph->backendNG, toolbar.durationEnable->IsChecked() ? toolbar.duration->GetSeconds() : -1);
});
toolbar.durationEnable->SetValue(true);
toolbar.durationEnable->Bind(wxEVT_CHECKBOX, [=](wxCommandEvent&){
toolbar.duration->Enable(toolbar.durationEnable->GetValue());
if(!toolbar.durationEnable->GetValue()) {
CHi_SetDuration(graph->backendNG, -1);
}
});
toolbar.btnPerform = new wxButton(tlba, wxID_ANY, "Compile");
toolbar.btnPerform->Bind(wxEVT_BUTTON, [=](wxCommandEvent &ev){
if(toolbar.btnPerform->GetLabel() == "Kill") {
CHi_StopCompilation(graph->backendNG);
toolbar.btnPerform->Disable();
tlba->EnableTool(wxID_SAVE, false);
tlba->EnableTool(wxID_OPEN, false);
} else {
CHi_BeginCompilation(graph->backendNG);
toolbar.btnPerform->SetLabel("Kill");
std::thread{[=](){
while(graph->backendNG->compilationStatus == CUTIHI_COMP_RUNNING) {
CallAfter([=](){
float t = CHi_Time_Get(graph->backendNG);
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));
}
CallAfter([=](){
stba->SetStatusText("Compilation has ended.");
});
}}.detach();
}
});
tlba->AddControl(toolbar.durationEnable);
tlba->AddControl(toolbar.duration);
tlba->AddControl(toolbar.btnPerform);
graph->backendNG->eventOnStopComplete = +[](CHiNodeGraph *ng){
wxTheApp->CallAfter([ng](){
wxButton *btn = ((Frame*) ng->ud)->toolbar.btnPerform;
btn->Enable();
btn->SetLabel("Compile");
auto tlba = ((Frame*) ng->ud)->tlba;
tlba->EnableTool(wxID_SAVE, true);
tlba->EnableTool(wxID_OPEN, true);
});
};
tlba->Realize();
aui.SetFlags(wxAUI_MGR_LIVE_RESIZE | wxAUI_MGR_DEFAULT);
@ -320,8 +552,8 @@ GrNode::GrNode(NodeGraph *parent) : wxPanel(parent, wxID_ANY, {0, 0}, {175, 80})
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();
newv.type = CUTIHI_VAL_TEXT;
newv.data.text = strdup(CHi_Microphone_GetSourceName((size_t) (uintptr_t) dlg.GetSelectionData()));
CHi_ConfigureSink(this->logical, p, newv);
parent->Dirtify(this);
}
@ -500,8 +732,8 @@ NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
{
GrNode *v = new GrNode(this);
v->logical = CHi_Preview();
v->name = "Preview";
v->sinks = {{"Video", GrNode::Port::Type::SAMPLE}};
ShapeGrNode(v);
CHi_RegisterNode(backendNG, v->logical);
gnodes.push_back(v);
@ -536,39 +768,18 @@ NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
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 {
@ -583,84 +794,47 @@ NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
} 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) {
ShapeGrNode(noed);
noed->Fit();
noed->SetPosition(position);
CHi_RegisterNode(backendNG, noed->logical);
@ -754,7 +928,7 @@ void NodeGraph::Dirtify(GrNode *g) {
}
}
if(g == gnodes[0]) {
if(g->logical->type == CUTIHI_T('CPre','view')) {
if(CHi_Hysteresis(g->logical)) {
CHiValue *val = CHi_Crawl(&g->logical->sinks[0]);
@ -787,52 +961,3 @@ 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);
}

View File

@ -18,19 +18,26 @@
struct NodeGraph;
struct ImageViewer;
struct CompositionSettings;
struct Timeline;
struct ctTimeCtrl;
struct Frame : wxFrame {
wxAuiManager aui;
ImageViewer *viewer;
NodeGraph *graph;
CompositionSettings *compsets;
Timeline *timeline;
wxStatusBar *stba;
wxToolBar *tlba;
struct {
ctTimeCtrl *duration;
wxCheckBox *durationEnable;
wxButton *btnPerform;
} toolbar;
Frame();
virtual ~Frame();
};
@ -59,17 +66,6 @@ struct GrNode : wxPanel {
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;