Add Save/Load functionality
This commit is contained in:
parent
34e7cd9362
commit
8f053bbdb1
7
Makefile
7
Makefile
@ -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`
|
||||
|
@ -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
BIN
btn_load.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
btn_save.bmp
Normal file
BIN
btn_save.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
@ -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, ¶ms, 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
487
hi/minitrace.c
Normal 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
276
hi/minitrace.h
Normal 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
317
hi/node.c
@ -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;
|
||||
}
|
||||
|
23
hi/node.h
23
hi/node.h
@ -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*);
|
||||
|
@ -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;
|
||||
|
14
hi/relay.c
14
hi/relay.c
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
31
hi/window.c
31
hi/window.c
@ -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;
|
||||
|
367
ui/frame.cpp
367
ui/frame.cpp
@ -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,14 +137,164 @@ 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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
22
ui/frame.h
22
ui/frame.h
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user