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

@@ -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;