commit 9e75ca73b288aa033496ec1574125cf73e0622a4 Author: Andrew Gundersen Date: Mon Jul 27 14:47:43 2020 -0500 Initial commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..e8ccd8c Binary files /dev/null and b/.DS_Store differ diff --git a/audio/.DS_Store b/audio/.DS_Store new file mode 100644 index 0000000..5f15c9f Binary files /dev/null and b/audio/.DS_Store differ diff --git a/audio/include/.DS_Store b/audio/include/.DS_Store new file mode 100644 index 0000000..052ae88 Binary files /dev/null and b/audio/include/.DS_Store differ diff --git a/audio/include/audio_handler.h b/audio/include/audio_handler.h new file mode 100644 index 0000000..b7dfe88 --- /dev/null +++ b/audio/include/audio_handler.h @@ -0,0 +1,14 @@ +#ifndef AUDIOHANDLER +#define AUDIOHANDLER + + +void init_audio (); + +void feed_output_buffer ( char *data, int len ); + +int send_audio_package (); + +void close_audio (); + + +#endif diff --git a/audio/include/frame_buffer.h b/audio/include/frame_buffer.h new file mode 100644 index 0000000..8ecddfa --- /dev/null +++ b/audio/include/frame_buffer.h @@ -0,0 +1,24 @@ +#ifndef FRAMEBUFFER +#define FRAMEBUFFER + + +typedef struct { + int frames_free, frames_used; + char *data, *cursor; +} frame_buffer; + + +void init_fb ( frame_buffer *fb ); + +void delete_fb ( frame_buffer *fb ); + +void clear_fb ( frame_buffer *fb ); + +void insert_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len ); + +void rewrite_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len ); + +char* read_frame_at_cursor_fb ( frame_buffer *fb ); + + +#endif diff --git a/audio/src/.DS_Store b/audio/src/.DS_Store new file mode 100644 index 0000000..653db42 Binary files /dev/null and b/audio/src/.DS_Store differ diff --git a/audio/src/audio_handler.c b/audio/src/audio_handler.c new file mode 100644 index 0000000..7251671 --- /dev/null +++ b/audio/src/audio_handler.c @@ -0,0 +1,348 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include "frame_buffer.h" +#include "package_handler.h" +#include "anims.h" + +#include "audio_handler.h" + + +#define SAMPLE_RATE (16000) +#define BUFFER_mSECS (30) +#define FRAMES_PER_BUFFER (160 * (BUFFER_mSECS/10)) + +#define NUM_CHANNELS (1) + + +/* Select sample format. */ +#define PA_SAMPLE_TYPE paInt16 +typedef short SAMPLE; +#define SAMPLE_SILENCE (0) +#define PRINTF_S_FORMAT "%d" + + + +static frame_buffer input_buffer; +static frame_buffer output_buffer; + +/* tri-state semaphore-esque variable: + * 0 - output_buffer is available + * 1 - output_buffer is being inserted into + * 2 - output_buffer is being read from */ +static int write_to_o_buf = 0; + +static bool streams_open; + +static bool voice_activate = false; +static int vad_streak = 0; + +static Fvad *vad; + +int send_audio_package (); +int send_close_package (); + +static int min_int (int a, int b) { + return (a < b) ? a : b; +} + + + + +static int o_callback( const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *fb ) +{ + (void) fb; + unsigned int i; + char *o_buf = (char *) outputBuffer; + + (void) inputBuffer; /* Prevent unused variable warnings. */ + (void) timeInfo; + (void) statusFlags; + + if (write_to_o_buf == 0) { + write_to_o_buf = 2; + if (output_buffer.frames_used > 0) { + int frames_to_read = min_int(framesPerBuffer, output_buffer.frames_used); + for (int i = 0; i < frames_to_read; i++) { + *((SAMPLE *)o_buf) = *((SAMPLE *) read_frame_at_cursor_fb(&output_buffer)); + o_buf += sizeof(SAMPLE); + } + } else { + *((SAMPLE *)o_buf) = 0; + } + write_to_o_buf = 0; + } + + return paContinue; +} + + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int i_callback( const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *fb ) +{ + (void) fb; + int i; + char *i_buf = (char *) inputBuffer; + + (void) outputBuffer; /* Prevent unused variable warnings. */ + (void) timeInfo; + (void) statusFlags; + + + i = fvad_process(vad, (int16_t *) i_buf, framesPerBuffer); + + if ( (i == 1) || voice_activate) { + if (!voice_activate) { + // set_anim(THINKING_ANIM); + puts("Recording..."); + } + if (i == 1) { + voice_activate = true; + vad_streak = 60; + } + insert_frames_fb(&input_buffer, i_buf, framesPerBuffer*sizeof(SAMPLE)); + } + + if (!voice_activate) { + rewrite_frames_fb(&input_buffer, i_buf, framesPerBuffer*sizeof(SAMPLE)); + } + + if (vad_streak >= 1) { + vad_streak -= 1; + + if (input_buffer.frames_used == (3*FRAMES_PER_BUFFER)) { + send_audio_package(); + } + + /* This means we had voice audio, but we no longer do so send what we got */ + if (vad_streak == 0) { + voice_activate = false; + // set_anim(PULSE_ANIM); + if (input_buffer.frames_used > 0) { + send_audio_package(); + } + send_close_package(); + } + } + + return paContinue; +} + + +static void handle_input_stream () { + PaStreamParameters inputParameters; + PaStream* istream; + PaError err = paNoError; + + init_fb(&input_buffer); + + + inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ + if (inputParameters.device == paNoDevice) { + fprintf(stderr,"Error: No default input device.\n"); + goto done; + } + inputParameters.channelCount = 1; /* mono input */ + inputParameters.sampleFormat = PA_SAMPLE_TYPE; + inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency; + inputParameters.hostApiSpecificStreamInfo = NULL; + + + /* Record some audio. -------------------------------------------- */ + err = Pa_OpenStream( + &istream, + &inputParameters, + NULL, /* &outputParameters, */ + SAMPLE_RATE, + FRAMES_PER_BUFFER, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + i_callback, + &input_buffer ); + if( err != paNoError ) goto done; + + err = Pa_StartStream( istream ); + if( err != paNoError ) goto done; + + while( (( err = Pa_IsStreamActive( istream ) ) == 1) && streams_open) + { + Pa_Sleep(1000); + } + + delete_fb(&input_buffer); + + if( err < 0 ) goto done; + + err = Pa_CloseStream( istream ); + if( err != paNoError ) goto done; + +done: + Pa_Terminate(); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + err = 1; /* Always return 0 or 1, but no other return codes. */ + } + printf("input audio stream error code (0 is SUCCESS): %d\n\n", err); +} + + +static void handle_output_stream () { + PaStreamParameters outputParameters; + PaStream* ostream; + PaError err = paNoError; + + init_fb(&output_buffer); + + + outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ + if (outputParameters.device == paNoDevice) { + fprintf(stderr,"Error: No default output device.\n"); + goto done; + } + + outputParameters.channelCount = 1; /* mono output */ + outputParameters.sampleFormat = PA_SAMPLE_TYPE; + outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + + + err = Pa_OpenStream( + &ostream, + NULL, + &outputParameters, /* &outputParameters, */ + SAMPLE_RATE, + FRAMES_PER_BUFFER, + paClipOff, + o_callback, + &output_buffer ); + if( err != paNoError ) goto done; + + err = Pa_StartStream( ostream ); + if( err != paNoError ) goto done; + + while(((err = Pa_IsStreamActive( ostream ) ) == 1) && streams_open) + { + Pa_Sleep(1000); + } + + delete_fb(&output_buffer); + + if( err < 0 ) goto done; + + err = Pa_CloseStream( ostream ); + if( err != paNoError ) goto done; + +done: + Pa_Terminate(); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + err = 1; /* Always return 0 or 1, but no other return codes. */ + } + printf("output audio stream error code (0 is SUCCESS): %d\n\n", err); +} + + +void init_audio () { + pthread_t in_th, out_th; + int err = paNoError; + + err = Pa_Initialize(); + if( err != paNoError ) { + fprintf(stderr, "error initializing port audio: %s\n", Pa_GetErrorText(err)); + exit(1); + } + + streams_open = true; + + vad = fvad_new(); + if (!vad) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + if (fvad_set_sample_rate(vad, SAMPLE_RATE) < 0) { + fprintf(stderr, "invalid mode\n"); + exit(1); + } + + if (fvad_set_mode(vad, 1) < 0) { + fprintf(stderr, "invalid mode\n"); + exit(1); + } + + puts("Creating input and output streams."); + sleep(1); + pthread_create(&in_th, NULL, (void *) handle_input_stream, NULL); + // pthread_create(&out_th, NULL, (void *) handle_output_stream, NULL); + + pthread_join(in_th, NULL); + // pthread_join(out_th, NULL); + + fvad_free(vad); +} + + + + +void feed_output_buffer (char * data, int len) { + + while (write_to_o_buf == 2) { + struct timespec t; + t.tv_sec = 0; + t.tv_nsec = 5000; + nanosleep(&t, &t); + } + write_to_o_buf = 1; + insert_frames_fb(&output_buffer, data, len); + + write_to_o_buf = 0; +} + + + +int send_audio_package () { + int err = 0; + if ((err = send_package(input_buffer.data, input_buffer.frames_used*sizeof(SAMPLE), 0))) { + printf("error sending audio package"); + } + clear_fb(&input_buffer); + return err; +} + + +int send_close_package () { + int err = 0; + if ((err = send_package("END", strlen("END"), 1))) { + fprintf(stderr, "error sending closing package"); + } + return err; +} + + + +void close_audio () { + streams_open = false; +} + diff --git a/audio/src/frame_buffer.c b/audio/src/frame_buffer.c new file mode 100644 index 0000000..9fcdac3 --- /dev/null +++ b/audio/src/frame_buffer.c @@ -0,0 +1,120 @@ +#include +#include +#include + + +#include "frame_buffer.h" + +#define BYTES_PER_FRAME (2) +#define INITIAL_FRAME_COUNT (480*100) + + +#define ALPHA (2) + + + +void init_fb ( frame_buffer *fb ) { + fb->frames_used = 0; + fb->frames_free = INITIAL_FRAME_COUNT; + fb->data = (char *) malloc(BYTES_PER_FRAME*INITIAL_FRAME_COUNT); + fb->cursor = fb->data; +} + + +void delete_fb ( frame_buffer *fb ) { + fb->frames_used = 0; + fb->frames_free = 0; + fb->cursor = NULL; + free(fb->data); +} + + +void clear_fb ( frame_buffer *fb ) { + free(fb->data); + fb->data = (char *) malloc(BYTES_PER_FRAME*INITIAL_FRAME_COUNT); + fb->cursor = fb->data; + fb->frames_free = INITIAL_FRAME_COUNT; + fb->frames_used = 0; +} + + +void insert_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len ) { + + int frames = frames_len / BYTES_PER_FRAME; + + int cursor_diff = fb->cursor - fb->data; + int data_len = (fb->frames_free+fb->frames_used)*BYTES_PER_FRAME; + + if (frames > fb->frames_free) { + fb->data = realloc(fb->data, (data_len+frames_len)*ALPHA); + fb->cursor = fb->data+cursor_diff; + + if (fb->frames_used*BYTES_PER_FRAME > data_len-cursor_diff) { + memmove(fb->data+data_len, fb->data, (fb->frames_used*BYTES_PER_FRAME)-(data_len-cursor_diff)); + } + fb->frames_used += frames; + fb->frames_free = fb->frames_used; + } + + data_len = (fb->frames_free+fb->frames_used)*BYTES_PER_FRAME; + if (fb->frames_used*BYTES_PER_FRAME > data_len-cursor_diff) { + memcpy(fb->data+((fb->frames_used*BYTES_PER_FRAME)-(data_len-cursor_diff)), frame_data, frames_len); + fb->frames_free -= frames; + fb->frames_used += frames; + return; + } + if (frames_len+(fb->frames_used*BYTES_PER_FRAME) > data_len-cursor_diff) { + int non_overlap = data_len - (cursor_diff+(fb->frames_used*BYTES_PER_FRAME)); + int overlap = frames_len - non_overlap; + memcpy(fb->cursor+(fb->frames_used*BYTES_PER_FRAME), frame_data, non_overlap); + memcpy(fb->data, &(frame_data[non_overlap]), overlap); + fb->frames_free -= frames; + fb->frames_used += frames; + return; + } + + memcpy(fb->cursor+(fb->frames_used*BYTES_PER_FRAME), frame_data, frames_len); + fb->frames_free -= frames; + fb->frames_used += frames; +} + + +void rewrite_frames_fb ( frame_buffer *fb, char *frame_data, int frames_len ) { + int frames = frames_len / BYTES_PER_FRAME; + + if (frames > (fb->frames_used + fb->frames_free)) { + fb->data = realloc(fb->data, frames_len*ALPHA); + fb->frames_free = frames; + fb->frames_used = frames; + } else { + fb->frames_free = fb->frames_free + fb->frames_used - frames; + fb->frames_used = frames; + } + memcpy(fb->data, frame_data, frames_len); + fb->cursor = fb->data; +} + + + +char* read_frame_at_cursor_fb ( frame_buffer *fb ) { + char *begin = fb->cursor; + int frames_free = fb->frames_free; + int frames_used = fb->frames_used; + char *data = fb->data; + + if (frames_used == 0) { + return NULL; + } + + fb->cursor += BYTES_PER_FRAME; + if (fb->cursor >= data + ((frames_free+frames_used)*BYTES_PER_FRAME)) { + fb->cursor = data; + } + + fb->frames_free += 1; + fb->frames_used -= 1; + + return begin; +} + + diff --git a/core/.DS_Store b/core/.DS_Store new file mode 100644 index 0000000..e827c27 Binary files /dev/null and b/core/.DS_Store differ diff --git a/core/Info.plist b/core/Info.plist new file mode 100644 index 0000000..20bb6d6 --- /dev/null +++ b/core/Info.plist @@ -0,0 +1,37 @@ + + + + + + CFBundleGetInfoString + 0.1.0, Copyright 2020 Crimata Technologies LLC + + CFBundleExecutable + Crimata + + CFBundleIdentifier + com.crimata.www + + CFBundleName + Crimata + + CFBundleIconFile + ICON.icns + + CFBundleShortVersionString + 0.01 + + CFBundleInfoDictionaryVersion + 6.0 + + CFBundlePackageType + APPL + + IFMajorVersion + 0 + + IFMinorVersion + 1 + + + \ No newline at end of file diff --git a/core/build/.DS_Store b/core/build/.DS_Store new file mode 100644 index 0000000..4eb442f Binary files /dev/null and b/core/build/.DS_Store differ diff --git a/core/build/apps/Crimata.app/Contents/Info.plist b/core/build/apps/Crimata.app/Contents/Info.plist new file mode 100644 index 0000000..20bb6d6 --- /dev/null +++ b/core/build/apps/Crimata.app/Contents/Info.plist @@ -0,0 +1,37 @@ + + + + + + CFBundleGetInfoString + 0.1.0, Copyright 2020 Crimata Technologies LLC + + CFBundleExecutable + Crimata + + CFBundleIdentifier + com.crimata.www + + CFBundleName + Crimata + + CFBundleIconFile + ICON.icns + + CFBundleShortVersionString + 0.01 + + CFBundleInfoDictionaryVersion + 6.0 + + CFBundlePackageType + APPL + + IFMajorVersion + 0 + + IFMinorVersion + 1 + + + \ No newline at end of file diff --git a/core/build/apps/Crimata.app/Contents/MacOS/Crimata b/core/build/apps/Crimata.app/Contents/MacOS/Crimata new file mode 100755 index 0000000..70a3752 Binary files /dev/null and b/core/build/apps/Crimata.app/Contents/MacOS/Crimata differ diff --git a/core/build/apps/Crimata.app/Contents/Resources/ICON.icns b/core/build/apps/Crimata.app/Contents/Resources/ICON.icns new file mode 100644 index 0000000..73e1e87 Binary files /dev/null and b/core/build/apps/Crimata.app/Contents/Resources/ICON.icns differ diff --git a/core/build/objects/src/main.o b/core/build/objects/src/main.o new file mode 100644 index 0000000..6a20433 Binary files /dev/null and b/core/build/objects/src/main.o differ diff --git a/core/include/main.h b/core/include/main.h new file mode 100644 index 0000000..83c06b0 --- /dev/null +++ b/core/include/main.h @@ -0,0 +1,31 @@ +#ifndef __TEST_MAIN +#define __TEST_MAIN + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +GtkBuilder *builder; +GObject *homeWindow; +GObject *homeBox; +GObject *crimataAiBox; + +char *render_text; + +enum anim_e; + +void * anim_handler (); + +int main (int argc, char *argv[]); + +#endif \ No newline at end of file diff --git a/core/makefile b/core/makefile new file mode 100644 index 0000000..c47b8ef --- /dev/null +++ b/core/makefile @@ -0,0 +1,88 @@ +################################################# +# +# Makefile - Test GUI +# +# Crimata Technologies 2020 +# +##################################################### + +CXX := -gcc +CXXFLAGS := `pkg-config --cflags gtk+-3.0` + + +LDFLAGS := \ + -L/usr/lib \ + -L/usr/local/lib \ + -lfvad \ + -lportaudio \ + +APP_NAME := Crimata +BUILD_DIR := ./build +OBJ_DIR := $(BUILD_DIR)/objects +APP_DIR := $(BUILD_DIR)/apps +EXE_DIR := $(BUILD_DIR)/apps/$(APP_NAME).app/Contents/MacOS +INCLUDE := \ + -I . \ + -I include/ \ + -I ../gui/include/ \ + -I ../networking/include/ \ + -I ../audio/include/ \ + -I /usr/include/gtk-3.0 + +SRC := \ + $(wildcard src/main.c) \ + $(wildcard ../gui/src/gtk-cairo_api.c) \ + $(wildcard ../gui/src/anims.c) \ + $(wildcard ../gui/src/eases.c) \ + $(wildcard ../networking/src/package_handler.c) \ + $(wildcard ../audio/src/audio_handler.c) \ + $(wildcard ../audio/src/frame_buffer.c) \ + + +OBJECTS := $(SRC:%.c=$(OBJ_DIR)/%.o) + + +# Rules +all: kill clean build $(EXE_DIR)/$(APP_NAME) run + + +# Compile all src files into object files into object directory +$(OBJ_DIR)/%.o: %.c + @mkdir -p $(@D) + $(CXX) $(CXXFLAGS) $(INCLUDE) -c $< -o $@ + + +# Link and make exe +$(EXE_DIR)/$(APP_NAME): $(OBJECTS) + @mkdir -p $(@D) + $(CXX) $(CXXFLAGS) -o $@ $^ `pkg-config --libs gtk+-3.0` $(LDFLAGS) + + +.PHONY: all build clean debug release + + +build: + @mkdir -p $(APP_DIR)/$(APP_NAME).app/Contents/{MacOS,Resources} + @mkdir -p $(OBJ_DIR) + @cp Info.plist $(APP_DIR)/$(APP_NAME).app/Contents/ + @cp resources/* $(APP_DIR)/$(APP_NAME).app/Contents/Resources/ + +# debug: CXXFLAGS += -DDEBUG -g +# debug: all + + +# release: CXXFLAGS += -O2 +# release: all + + +run: $(EXE_DIR)/$(APP_NAME) + # remove object file directories + rm -rvf $(BUILD_DIR)/audio $(BUILD_DIR)/gui $(BUILD_DIR)/networking +# $< + + +clean: + -@rm -rvf $(BUILD_DIR)/* + +kill: + @pkill $(APP_NAME) || true diff --git a/core/resources/.DS_Store b/core/resources/.DS_Store new file mode 100644 index 0000000..1375bbf Binary files /dev/null and b/core/resources/.DS_Store differ diff --git a/core/resources/ICON.icns b/core/resources/ICON.icns new file mode 100644 index 0000000..73e1e87 Binary files /dev/null and b/core/resources/ICON.icns differ diff --git a/core/src/main.c b/core/src/main.c new file mode 100644 index 0000000..e1c24eb --- /dev/null +++ b/core/src/main.c @@ -0,0 +1,172 @@ +/* +** Crimata Technologies - 2020 +** +** Client Main +** +** +** This is the top-level file for the client program. It is the home +** of main (), which spawns all the nessesary threads and ensures +** the program shutsdown correctly. +** +** The functions are as follows: +** +** **********************FUNCTIONS SEMANTICS************************ +** +** 1. main () : spawns three additional threads for audio, networking +** and the "main loop" in addition to initiating the GTK gui. +** +** @ - +** @@ - + */ + +#include + +GtkBuilder *builder; +GObject *homeWindow; +GObject *homeBox; +GObject *crimataAiBox; + +pthread_t drawing_thread; +pthread_mutex_t mutex; + +enum anim_e +{ BOUNCE_ANIM, STANDBY_ANIM }; + +static char* user_id; + +// static bool exit = false; + +void * anim_handler () +{ + //! This will run indefinately, which is fine for the Version 0.1.0. + //! This anim will render any value that is at the mem loc of render_text. + print_anim (); + + // while (!exit) //! Will return null once exit is true. + // { + // switch (current_anim) + // { + // case BOUNCE_ANIM: + // bounce_anim (); + // break; + + // case STANDBY_ANIM: + // standby_anim (); + // break; + // } + // } + return 0; +} + +void * server_connect (void *ptr) +{ + puts("Connecting to the server!"); + + if (connect_socket ( "127.0.0.1", 1040 ) < 0) { + puts("error connecting to socket"); + exit(1); + } + + /* Send the user_id to the backend. */ + user_id = (char *) malloc(2); + strcpy(user_id, "1"); + send_package(user_id, strlen(user_id), 2); + + return 0; +} + +void * main_loop (void *ptr) +{ + sleep(1); + + bool exit = false; + + // recv_package() will write to this variable. + char *package_response = malloc(sizeof(int)); + + while (!exit) { + + int mes_len; + int mes_type = recv_package(&package_response, &mes_len); + + switch (mes_type) { + + case 0: + printf("audio message length: %d\n", mes_len); + feed_output_buffer(package_response, mes_len); + break; + + case 1: + if (strcmp(package_response, "EXIT") == 0) { + exit = true; + } else { + //! write to render_text memory address. + render_text = (char *) malloc(sizeof package_response); + strcpy(render_text, (const char *) package_response); + puts((const char *) package_response); + } + break; + + case -1: + puts("error parsing package"); + break; + } + } + + free(package_response); + + return 0; +} + +int main (int argc, char *argv[]) +{ + pthread_t _admin, _main, _audio; + + //! Launch non-UI threads before initializing the UI. + //! Le9t's simply try connecting to the server. + //! Now, let's kickstarting the main loop. + pthread_create(&_admin, NULL, (void *) server_connect, NULL); + pthread_create(&_main, NULL, (void *) main_loop, NULL); + pthread_create(&_audio, NULL, (void *) init_audio, NULL); + + + gtk_init (&argc, &argv); + + GError *error = NULL; + builder = gtk_builder_new (); + gtk_builder_add_from_file (builder, "../gui/src/window.ui", &error); + + /* Set up hooks for relevant UI elements. */ + homeWindow = gtk_builder_get_object (builder, "homeWindow"); + homeBox = gtk_builder_get_object (builder, "homeBox"); + crimataAiBox = gtk_builder_get_object (builder, "crimataAiBox"); + + /* Allocate area of window to have drawing functionality and set dims. */ + create_cairo_surface(crimataAiBox); + + /* CrimataAi animations */ + pthread_mutex_init (&mutex, NULL); + pthread_create (&drawing_thread, NULL, anim_handler, NULL); + + /* Create a timer to invalidate (refresh) crimataAiBox at 60Hz + ** Needs to only be called when crimataAiBox is showing. + */ + g_timeout_add (1000 / 60, invalidate_cb, (void *) crimataAiBox); + + /* Assign UI opertaions using GTK singals */ + g_signal_connect (homeWindow, "destroy", G_CALLBACK (gtk_main_quit), NULL); + g_signal_connect (crimataAiBox, "draw", G_CALLBACK (drawing_area_draw_cb), NULL); + g_signal_connect (crimataAiBox, "configure-event", G_CALLBACK (resize_cairo_surface_cb), NULL); + + gtk_main(); + + + close_audio(); + close_socket(); + + pthread_join(_audio, NULL); + pthread_join(_admin, NULL); + // pthread_join(_main, NULL); + + return 0; +} \ No newline at end of file diff --git a/gui/include/anims.h b/gui/include/anims.h new file mode 100644 index 0000000..160c834 --- /dev/null +++ b/gui/include/anims.h @@ -0,0 +1,28 @@ +#ifndef ANIMS +#define ANIMS + +#include +#include +#include + +extern cairo_surface_t *surface; +extern int surface_width; +extern int surface_height; + +extern char *render_text; + +extern pthread_mutex_t mutex; + +void * test_anim (); + +void * print_anim (); + +void * start_up_anim (); + +void * recording_anim (); + +void * standby_anim (); + +void * bounce_anim (); + +#endif \ No newline at end of file diff --git a/gui/include/eases.h b/gui/include/eases.h new file mode 100644 index 0000000..75af4f4 --- /dev/null +++ b/gui/include/eases.h @@ -0,0 +1,46 @@ +#ifndef __EASES +#define __EASES + +#include + +/* Easing Math Utilites */ + +float quadratic_ease_in_out(double p, int distance); + +float elastic_ease_out(float p); + +float quadratic_ease_in(float p); + +float quadratic_ease_out(float p); + +float sine_ease_out(float p); + +float back_ease_out(float p); + +float exponential_ease_out(float p); + +float sine_wave (float counter, float f, int A); + +float sin_wave_scaled (float counter, float f, int A); + +// float asin_wave_scaled (float counter, float f, int A, float t); + +float cos_wave (float counter, float f, int A, float t); + +float gravity_wave (float counter, float f, int A, float t); + +float gravity_wave_cos (float counter, float w, float ampl); + +struct Strobes +{ + int positionY; + int positionX; + + float velocityY; + float velocityX; +}; + +void damped_oscillator (float anchorX, float anchorY, float k, struct Strobes* circle); + + +#endif diff --git a/gui/include/gtk-cairo_api.h b/gui/include/gtk-cairo_api.h new file mode 100644 index 0000000..3747508 --- /dev/null +++ b/gui/include/gtk-cairo_api.h @@ -0,0 +1,61 @@ +#ifndef GTK_CAIRO_API +#define GTK_CAIRO_API + +/* +** Crimata Technologies - 2020 +** +** Client Gtk-Cairo API +** +** +** An API that serves as a boilerplate for rendering graphics in the +** center of a given GObject or GTKWidget. +** +** The semantics are as follows: + +** **********************VARIABLES SEMANTICS************************ + +** 1. @cairo_surface_t *surface : tells functions to look for the +** definition of surface in your code. +** +** 2. pthread_mutex_t mutex : Gives Mutual (MUT) Exclustion (EX) +** functionality to functions so that they don't access surface +** and context variables at the same time. + +** **********************FUNCTIONS SEMANTICS************************ + +** +** 3. @@invalidate_cb () : Calls gtk_widget_queue_draw() on a given +** widget so that GTK knows to refresh it. +** +** 4. create_cairo_surface () : Allocate a region of the window to +** create (technically update) the surface while also updating its +** surface_width and surface_height variables. +** +** 5. drawing_area_draw_cb () : Set this function up in a callback +** so that it can refresh and paint over the cairo source every +** "frame" to obtain the animation effect. +** +** 6. resize_cairo_surface_cb () : Effectivly the same as create_- +** cairo_surface but is called everytime the window gets resized. +** +** @ - https://cairographics.org/manual/cairo-cairo-surface-t.html +** @@ - _cb in name means should be used as a GTK callback. + */ + +#include + +extern cairo_surface_t *surface; +extern int surface_width; +extern int surface_height; + +extern pthread_mutex_t mutex; + +gboolean invalidate_cb (gpointer widget); + +void create_cairo_surface (GObject* uiBox); + +void drawing_area_draw_cb (GtkWidget *widget, cairo_t *context); + +void resize_cairo_surface_cb (GtkWidget *widget, GdkEvent *event); + +#endif \ No newline at end of file diff --git a/gui/src/anims.c b/gui/src/anims.c new file mode 100644 index 0000000..ca24124 --- /dev/null +++ b/gui/src/anims.c @@ -0,0 +1,81 @@ +#include + +#include "anims.h" +#include "eases.h" + +static const int frames_per_sec = 60; + +void * init_context () +{ + pthread_mutex_lock (&mutex); + cairo_t *cr = cairo_create (surface); + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_rectangle (cr, 0, 0, surface_width, surface_height); + cairo_fill (cr); + cairo_translate (cr, surface_width/2, surface_height/2); + return cr; +} + +void * destroy_context (cairo_t *cr) +{ + cairo_destroy (cr); + pthread_mutex_unlock (&mutex); + usleep (1E6 / frames_per_sec); + return 0; +} + +/* Animate a logo through multiple calls and adjusting x y cords. +** X's and Y's @ 0 means logo is at default position: +** +** * +** * * +*/ +void logo (cairo_t *cr, float x1, // circle top + float y1, + float x2, // circle bottom left + float y2, + float x3, // circle bottom right + float y3 + ) +{ + // circle top + cairo_arc (cr, x1+0, y1-10, 7.25, 0, 6.28); + cairo_fill (cr); + + // circle bottom left + cairo_arc (cr, x2-9.34, y2+6, 7.25, 0, 6.28); + cairo_fill (cr); + + // circle bottom right + cairo_arc (cr, x3+9.34, y3+6, 7.25, 0, 6.28); + cairo_fill (cr); +} + +// #include "start_up_anim.c" +#include "recording_anim.c" +#include "standby_anim.c" +#include "bounce_anim.c" + +void * print_anim () +{ + while (1) + { + cairo_t *cr = init_context (); + + //! Render text based on global text variable. + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_text_extents_t te; + cairo_select_font_face (cr, "Montserrat", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 20); + cairo_text_extents (cr, render_text, &te); + cairo_move_to (cr, te.width/-2, te.height/2); + cairo_show_text (cr, render_text); + + destroy_context (cr); + } +} + + + + + diff --git a/gui/src/bounce_anim.c b/gui/src/bounce_anim.c new file mode 100644 index 0000000..f53da26 --- /dev/null +++ b/gui/src/bounce_anim.c @@ -0,0 +1,33 @@ +void * bounce_anim () +{ + cairo_t *cr = init_context (); + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1.0); + + logo (cr, 0, 0, 0, 0, 0, 0); + + destroy_context (cr); + + sleep(1); + + int counter = 0; + while (1) + { + cairo_t *cr = init_context (); + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1.0); + + float e = sine_wave (counter, 1.5, 30); + e = fabsf(e); + logo (cr, 0, // circle top + -e, + 0, // circle bottom left + -e, + 0, // circle bottom right + -e + ); + + destroy_context (cr); + counter++; + } + + return 0; +} \ No newline at end of file diff --git a/gui/src/eases.c b/gui/src/eases.c new file mode 100644 index 0000000..e00050d --- /dev/null +++ b/gui/src/eases.c @@ -0,0 +1,128 @@ +#include + +#include +#include +#include + +// Modeled after the piecewise quadratic +// y = (1/2)((2x)^2) ; [0, 0.5) +// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1] +float quadratic_ease_in_out(double p, int distance) +{ + double s = p / distance; + + if(s < 0.5) + { + return distance * 2 * s * s; + } + else + { + return distance * ((-2 * s * s) + (4 * s) - 1); + } +} + +// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1 +float elastic_ease_out(float p) +{ + return sin(-13 * M_PI_2 * (p + 1)) * pow(2, -10 * p) + 1; +} + +// Modeled after the parabola y = x^2 +float quadratic_ease_in(float p) +{ + return p * p; +} + +// Modeled after the parabola y = -x^2 + 2x +float quadratic_ease_out(float p) +{ + return -(p * (p - 2)); +} + +// Modeled after quarter-cycle of sine wave (different phase) +float sine_ease_out(float p) +{ + return sin(p * M_PI_2); +} + +// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi)) +float back_ease_out(float p) +{ + float f = (1 - p); + return 1 - (f * f * f - f * sin(f * M_PI)); +} + +// Modeled after the exponential function y = -2^(-10x) + 1 +float exponential_ease_out(float p) +{ + return (p == 1.0) ? p : 1 - pow(2, -10 * p); +} + +float sine_wave (float counter, float f, int A) +{ + float t = counter * 0.01; + return A * sin (2 * M_PI * f * t); +} + +float sin_wave_scaled (float counter, float f, int A) +{ + int t = counter; + return A * sin (2 * M_PI * f * t); +} + +// float asin_wave_scaled (float counter, float f, int A, float t) +// { +// t = counter; +// return A * asin (2 * M_PI * f * t); +// } + + +float cos_wave (float counter, float f, int A, float t) +{ + t = counter * 0.01; + return A * cos (2 * M_PI * f * t); +} + +float gravity_wave (float counter, float f, int A, float t) +{ + t = counter; + float x = A * sin (2 * M_PI * f * t); + return fabsf(x); +} + +float gravity_wave_cos (float counter, float w, float ampl) +{ + float t = 0.5; + float x = ampl * cos ( counter * w * t ); + return fabsf(x); +} + +void damped_oscillator (float anchorX, float anchorY, float k, struct Strobes* circle) +{ + + // constants + int gravity = 0; + int mass = 10; + float timeStep = 0.28; + float damping = 10; + + // calculate force Y + float springForceY = -k*(circle->positionY - anchorY); + float dampingForceY = damping * circle->velocityY; + float forceY = springForceY + mass * gravity - dampingForceY; + + // calculate position Y + float accelerationY = forceY/mass; + circle->velocityY = circle->velocityY + accelerationY * timeStep; + circle->positionY = circle->positionY + circle->velocityY * timeStep; + + // calculate force X + float springForceX = -k*(circle->positionX - anchorX); + float dampingForceX = damping * circle->velocityX; + float forceX = springForceX - dampingForceX; + + // calculate position X + float accelerationX = forceX/mass; + circle->velocityX = circle->velocityX + accelerationX * timeStep; + circle->positionX = circle->positionX + circle->velocityX * timeStep; +} diff --git a/gui/src/gtk-cairo_api.c b/gui/src/gtk-cairo_api.c new file mode 100644 index 0000000..8a8cd89 --- /dev/null +++ b/gui/src/gtk-cairo_api.c @@ -0,0 +1,60 @@ +#include + +cairo_surface_t *surface; +int surface_width; +int surface_height; + +// // simply call gtk_widget_queue_draw on crimataAiBox +gboolean invalidate_cb (gpointer uiBox) +{ + gtk_widget_queue_draw (GTK_WIDGET (uiBox)); + return TRUE; +} + +// refresh the drawing surface +void drawing_area_draw_cb (GtkWidget *widget, cairo_t *context) +{ + // copy the contents of the surface to the current context + pthread_mutex_lock (&mutex); + + if (surface != (cairo_surface_t *)NULL) + { + cairo_set_source_rgb (context, 1, 1, 1); + cairo_set_source_surface (context, surface, 0, 0); + cairo_paint (context); + } + + pthread_mutex_unlock (&mutex); +} + +void create_cairo_surface (GObject* uiBox) +{ + GtkAllocation allocation; + gtk_widget_get_allocation ((GtkWidget*) uiBox, &allocation); + + // create the surface -> what we are going to be painting on + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, allocation.width, + allocation.height); + + surface_width = allocation.width; + surface_height = allocation.height; +} + +/* Create drawing surface on crimataAiBox during Gtk config event. */ +void resize_cairo_surface_cb (GtkWidget *widget, GdkEvent *event) +{ + if (event->type == GDK_CONFIGURE) + { + pthread_mutex_lock (&mutex); + + GtkAllocation allocation; + gtk_widget_get_allocation (widget, &allocation); + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, allocation.width, + allocation.height); + + surface_width = allocation.width; + surface_height = allocation.height; + + pthread_mutex_unlock (&mutex); + } +} \ No newline at end of file diff --git a/gui/src/print_anim.c b/gui/src/print_anim.c new file mode 100644 index 0000000..436982d --- /dev/null +++ b/gui/src/print_anim.c @@ -0,0 +1,18 @@ +void * print_anim () +{ + while (1) + { + cairo_t *cr = init_context (); + + //! Render text based on global text variable. + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, counter/200); + cairo_text_extents_t te; + cairo_select_font_face (cr, "Montserrat", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 20); + cairo_text_extents (cr, "Crimata", &te); + cairo_move_to (cr, te.width/-2+25, te.height/2-4); + cairo_show_text (cr, "Crimata"); + + destroy_context (cr); + } +} \ No newline at end of file diff --git a/gui/src/recording_anim.c b/gui/src/recording_anim.c new file mode 100644 index 0000000..1b67541 --- /dev/null +++ b/gui/src/recording_anim.c @@ -0,0 +1,228 @@ +/* Scatter dots and move relative to sound effects and +** the transcription of the user's voice dynamically renders +** on screen. +** +** +** ▼ +** * ▼ * +** ▲ * ▲ +** ▲ +** +** +** "Hey Andrew, how's..." +** +*/ +void * recording_anim () +{ + struct Strobes circle_1; + circle_1.positionY=0; + circle_1.positionX=0; + + circle_1.velocityY=0; + circle_1.velocityX=0; + + struct Strobes circle_2; + circle_2.positionY=0; + circle_2.positionX=0; + + circle_2.velocityY=0; + circle_2.velocityX=0; + + struct Strobes circle_3; + circle_3.positionY=0; + circle_3.positionX=0; + + circle_3.velocityY=0; + circle_3.velocityX=0; + + struct Strobes circle_4; + circle_4.positionY=0; + circle_4.positionX=0; + + circle_4.velocityY=0; + circle_4.velocityX=0; + + struct Strobes circle_5; + circle_5.positionY=0; + circle_5.positionX=0; + + circle_5.velocityY=0; + circle_5.velocityX=0; + + struct Strobes circle_6; + circle_6.positionY=0; + circle_6.positionX=0; + + circle_6.velocityY=0; + circle_6.velocityX=0; + + cairo_t *cr = init_context (); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + + cairo_arc (cr, 0, -10, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, -9.34, 6, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, 9.34, 6, 7.25, 0, 6.28); + cairo_fill (cr); + + destroy_context (cr); + + sleep(1); + + // first damped oscillator animation + static float k = 30; + for (float c=0; c<1; c+=0.02) + { + cairo_t *cr = init_context (); + + float c_elastic = exponential_ease_out (c); + + + + + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + float x1 = -1*c_elastic*30; + float y1 = x1/4; + cairo_arc (cr, x1-7+2, (y1)-8, 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.75); + damped_oscillator( x1, y1, k, &circle_1 ); + cairo_arc (cr, circle_1.positionX-7+2, (circle_1.positionY)-8, 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.5); + damped_oscillator( circle_1.positionX, circle_1.positionY, k, &circle_2 ); + cairo_arc (cr, circle_2.positionX-7+2, (circle_2.positionY)-8, 7, 0, 6.28); + cairo_fill (cr); + + + + + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + float x2 = c_elastic*30/2; + float y2 = -1*x2; + cairo_arc (cr, x2-7+2, y2+8, 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.75); + damped_oscillator( x2, y2, k, &circle_3 ); + cairo_arc (cr, circle_3.positionX-7+2, (circle_3.positionY)+8, 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.5); + damped_oscillator( circle_3.positionX, circle_3.positionY, k, &circle_4 ); + cairo_arc (cr, circle_4.positionX-7+2, (circle_4.positionY)+8, 7, 0, 6.28); + cairo_fill (cr); + + + + + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + float x3 = c_elastic*30+10; + float y3 = -1*x3/4; + cairo_arc (cr, x3+7+2, y3, 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.75); + damped_oscillator( x3, y3, k, &circle_5 ); + cairo_arc (cr, circle_5.positionX+7+2, (circle_5.positionY), 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.5); + damped_oscillator( circle_5.positionX, circle_5.positionY, k, &circle_6 ); + cairo_arc (cr, circle_6.positionX+7+2, (circle_6.positionY), 7, 0, 6.28); + cairo_fill (cr); + + + + + destroy_context (cr); + } + + for (float c=0; c<10; c+=0.01) + { + cairo_t *cr = init_context (); + + + + + + //! where we will input sound values + float wav_y = sin_wave_scaled (c, 1.2, 20); + float wav_y_2 = sin_wave_scaled (c, 1, 10); + float wav_y_3 = sin_wave_scaled (c, 1, 25); + + + + + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + float x1 = -30; + float y1 = wav_y-7.5; + cairo_arc (cr, x1-7+2, (y1)-8, 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.75); + damped_oscillator( x1, y1, k, &circle_1 ); + cairo_arc (cr, circle_1.positionX-7+2, (circle_1.positionY)-8, 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.5); + damped_oscillator( circle_1.positionX, circle_1.positionY, k, &circle_2 ); + cairo_arc (cr, circle_2.positionX-7+2, (circle_2.positionY)-8, 7, 0, 6.28); + cairo_fill (cr); + + + + + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + float x2 = 15; + float y2 = wav_y_2-15; + cairo_arc (cr, x2-7+2, y2+8, 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.75); + damped_oscillator( x2, y2, k, &circle_3 ); + cairo_arc (cr, circle_3.positionX-7+2, (circle_3.positionY)+8, 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.5); + damped_oscillator( circle_3.positionX, circle_3.positionY, k, &circle_4 ); + cairo_arc (cr, circle_4.positionX-7+2, (circle_4.positionY)+8, 7, 0, 6.28); + cairo_fill (cr); + + + + + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + float x3 = 40; + float y3 = wav_y_3-10; + cairo_arc (cr, x3+7+2, y3, 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.75); + damped_oscillator( x3, y3, k, &circle_5 ); + cairo_arc (cr, circle_5.positionX+7+2, (circle_5.positionY), 7, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.5); + damped_oscillator( circle_5.positionX, circle_5.positionY, k, &circle_6 ); + cairo_arc (cr, circle_6.positionX+7+2, (circle_6.positionY), 7, 0, 6.28); + cairo_fill (cr); + + + + + + destroy_context (cr); + } + return 0; +} \ No newline at end of file diff --git a/gui/src/standby_anim.c b/gui/src/standby_anim.c new file mode 100644 index 0000000..5418121 --- /dev/null +++ b/gui/src/standby_anim.c @@ -0,0 +1,134 @@ +/* On start-up animation; poster-child Crimata animation. +** +** +** +** +*/ +void * standby_anim () +{ + /* Hold starting point for 1s. */ + cairo_t *cr = init_context (); + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1.0); + + logo (cr, 0, 0, 0, 0, 0, 0); + + destroy_context (cr); + + sleep(1); + + while (1) + { + /* Pulse-out 12 points slow. */ + for (float counter=0; counter<12; counter+=0.5) + { + cairo_t *cr = init_context (); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1.0); + + int e = quadratic_ease_in_out (counter, 12); + logo (cr, 0, // circle top + -e, + -e, // circle bottom left + e, + e, // circle bottom right + e + ); + + destroy_context (cr); + } + + sleep (1); + + /* Pulse-in 12 points fast */ + for (float counter=0; counter<12; counter+=2) + { + cairo_t *cr = init_context (); + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1.0); + + /* Begin @ position where the last anim left off */ + //! might be good thing to automate + int offset = 11; + int e = quadratic_ease_in_out (counter, 12); + logo (cr, 0, // circle top + e-offset, + e-offset, // circle bottom left + -e+offset, + -e+offset, // circle bottom right + -e+offset + ); + + destroy_context (cr); + } + + /* Accellerate rotation. */ + for (float speed=0; speed<1; speed+=0.1) + { + cairo_t *cr = init_context (); + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1.0); + + float v = quadratic_ease_in (speed); + cairo_rotate(cr, 10*v); + + logo (cr, 0, 0, 0, 0, 0, 0); + + destroy_context (cr); + } + + /* Decellerate rotation. */ + for (float speed=0; speed<1; speed+=0.01) + { + cairo_t *cr = init_context (); + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1.0); + + float v = sine_ease_out (speed); + cairo_rotate(cr, 80*v-2.5); + + logo (cr, 0, 0, 0, 0, 0, 0); + + destroy_context (cr); + } + + sleep (1); + + /* Pulse-out-in 8 points twice. */ + for (float c=0; c<2; c++) + { + for (float counter=0; counter<8; counter+=1) + { + cairo_t *cr = init_context (); + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1.0); + + int e = quadratic_ease_in_out (counter, 8); + logo (cr, 0, // circle top + -e, + -e, // circle bottom left + e, + e, // circle bottom right + e + ); + + destroy_context (cr); + } + + for (float counter=0; counter<8; counter+=1) + { + cairo_t *cr = init_context (); + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1.0); + + int offset = 7; + int e = quadratic_ease_in_out (counter, 8); + logo (cr, 0, // circle top + e-offset, + e-offset, // circle bottom left + -e+offset, + -e+offset, // circle bottom right + -e+offset + ); + + destroy_context (cr); + } + } + sleep(8); + } + return 0; +} \ No newline at end of file diff --git a/gui/src/start_up_anim.c b/gui/src/start_up_anim.c new file mode 100644 index 0000000..e9dac81 --- /dev/null +++ b/gui/src/start_up_anim.c @@ -0,0 +1,195 @@ +void * start_up_anim () +{ + sleep (1); + static const char *render_text = "Good Morning"; + + for (float counter=0; counter<100; counter+=2) + { + int x_dist = 100; + + cairo_t *cr = init_context (); + + cairo_scale(cr, 0.9, 0.9); + + /* X-Axis Ease */ + float ease_x = quadratic_ease_in_out(counter, x_dist); + + /* Logo */ + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, counter/x_dist); + cairo_arc (cr, ease_x, -10, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, ease_x-9.34, 6, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, ease_x+9.34, 6, 7.25, 0, 6.28); + cairo_fill (cr); + + destroy_context (cr); + } + + for (float counter=0; counter<200; counter+=10) + { + int last_pos_x = 100; + int x_dist = 185; + + cairo_t *cr = init_context (); + + cairo_scale(cr, 0.9, 0.9); + + double ease_x = quadratic_ease_in_out(counter, x_dist); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_arc (cr, (ease_x*-1)+last_pos_x, -10, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, (ease_x*-1)+last_pos_x-9.34, 6, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, (ease_x*-1)+last_pos_x+9.34, 6, 7.25, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, counter/200); + cairo_text_extents_t te; + cairo_select_font_face (cr, "Montserrat", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 40); + cairo_text_extents (cr, "Crimata", &te); + cairo_move_to (cr, te.width/-2+25, te.height/2-4); + cairo_show_text (cr, "Crimata"); + + destroy_context (cr); + } + + for (float counter=0; counter<10; counter+=1) + { + int last_pos_x = -85; + int y_dist = 10; + + cairo_t *cr = init_context (); + + cairo_scale(cr, 0.9, 0.9); + + double ease_y = quadratic_ease_in_out(counter, y_dist); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_arc (cr, last_pos_x, ease_y-10, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, last_pos_x-9.34, ease_y+6, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, last_pos_x+9.34, ease_y+6, 7.25, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_text_extents_t te; + cairo_select_font_face (cr, "Montserrat", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 40); + cairo_text_extents (cr, "Crimata", &te); + cairo_move_to (cr, te.width/-2+25, te.height/2-4); + cairo_show_text (cr, "Crimata"); + + destroy_context (cr); + } + + for (float counter=0; counter<15; counter+=1) + { + int last_pos_x = -85; + int last_pos_y = 10; + int y_dist = 15; + + cairo_t *cr = init_context (); + + cairo_scale(cr, 0.9, 0.9); + + double ease_y = quadratic_ease_in_out(counter, y_dist); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_arc (cr, last_pos_x, (ease_y*-1)+last_pos_y-10, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, last_pos_x-9.34, (ease_y*-1)+last_pos_y+6, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, last_pos_x+9.34, (ease_y*-1)+last_pos_y+6, 7.25, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_text_extents_t te; + cairo_select_font_face (cr, "Montserrat", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 40); + cairo_text_extents (cr, "Crimata", &te); + cairo_move_to (cr, te.width/-2+25, te.height/2-4); + cairo_show_text (cr, "Crimata"); + + destroy_context (cr); + } + + for (float counter=0; counter<5; counter+=0.25) + { + int last_pos_x = -85; + int last_pos_y = -5; + int y_dist = 5; + + cairo_t *cr = init_context (); + + cairo_scale(cr, 0.9, 0.9); + + double ease_y = quadratic_ease_in_out(counter, y_dist); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_arc (cr, last_pos_x, ease_y+last_pos_y-10, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, last_pos_x-9.34, ease_y+last_pos_y+6, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, last_pos_x+9.34, ease_y+last_pos_y+6, 7.25, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_text_extents_t te; + cairo_select_font_face (cr, "Montserrat", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 40); + cairo_text_extents (cr, "Crimata", &te); + cairo_move_to (cr, te.width/-2+25, te.height/2-4); + cairo_show_text (cr, "Crimata"); + + // Render text beta. + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_select_font_face (cr, "Montserrat", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 12); + cairo_text_extents (cr, render_text, &te); + cairo_move_to (cr, te.width/-2, te.height/2+60); + cairo_show_text (cr, render_text); + + destroy_context (cr); + } + + while ( 1 ) + { + int last_pos_x = -85; + int last_pos_y = 0; + + cairo_t *cr = init_context (); + + cairo_scale(cr, 0.9, 0.9); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_arc (cr, last_pos_x, last_pos_y-10, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, last_pos_x-9.34, last_pos_y+6, 7.25, 0, 6.28); + cairo_fill (cr); + cairo_arc (cr, last_pos_x+9.34, last_pos_y+6, 7.25, 0, 6.28); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_text_extents_t te; + cairo_select_font_face (cr, "Montserrat", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 40); + cairo_text_extents (cr, "Crimata", &te); + cairo_move_to (cr, te.width/-2+25, te.height/2-4); + cairo_show_text (cr, "Crimata"); + + // Render text beta. + cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 1); + cairo_select_font_face (cr, "Montserrat", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 12); + cairo_text_extents (cr, render_text, &te); + cairo_move_to (cr, te.width/-2, te.height/2+60); + cairo_show_text (cr, render_text); + + destroy_context (cr); + } + return 0; +} diff --git a/gui/src/window 2.css b/gui/src/window 2.css new file mode 100644 index 0000000..68888a3 --- /dev/null +++ b/gui/src/window 2.css @@ -0,0 +1,55 @@ +.font { + font-family: Verdana; +} + +.cssTest { + color: #383838; + font-size: 25px; + font-weight: bold; +} + +.loginButton { + color: #383838; + font-weight: normal; + border-radius: 20px; + border: none; + background-image: linear-gradient(45deg, #F6F5F4, #F6F5F4); +} + +.loginButton:hover { + text-decoration: none; +} + +.window { + background-color: white; +/* border-radius: 20px; +*/} + +.version { + color: gray; +} + +.titlebar { + border-radius: 0px; + background-image: linear-gradient(45deg, #F6F5F4, #F6F5F4); + border-color: white; +} + +.homeHeaderLabel { + color: #383838; + font-size: 20px; + font-weight: bold; +} + +.settingsButton { + border-radius: 20px; +} + +.settingsButton:hover { + text-decoration: none; +} + +/*.customCloseButton { + border-radius: 20; + height: 5px; +}*/ diff --git a/gui/src/window 2.ui b/gui/src/window 2.ui new file mode 100644 index 0000000..d3ed2d4 --- /dev/null +++ b/gui/src/window 2.ui @@ -0,0 +1,31 @@ + + + + + + True + False + Crimata Tech + center + 600 + 350 + center + + + True + True + 0 + 0 + + + + + + + + + diff --git a/gui/src/window.css b/gui/src/window.css new file mode 100644 index 0000000..68888a3 --- /dev/null +++ b/gui/src/window.css @@ -0,0 +1,55 @@ +.font { + font-family: Verdana; +} + +.cssTest { + color: #383838; + font-size: 25px; + font-weight: bold; +} + +.loginButton { + color: #383838; + font-weight: normal; + border-radius: 20px; + border: none; + background-image: linear-gradient(45deg, #F6F5F4, #F6F5F4); +} + +.loginButton:hover { + text-decoration: none; +} + +.window { + background-color: white; +/* border-radius: 20px; +*/} + +.version { + color: gray; +} + +.titlebar { + border-radius: 0px; + background-image: linear-gradient(45deg, #F6F5F4, #F6F5F4); + border-color: white; +} + +.homeHeaderLabel { + color: #383838; + font-size: 20px; + font-weight: bold; +} + +.settingsButton { + border-radius: 20px; +} + +.settingsButton:hover { + text-decoration: none; +} + +/*.customCloseButton { + border-radius: 20; + height: 5px; +}*/ diff --git a/gui/src/window.ui b/gui/src/window.ui new file mode 100644 index 0000000..d3ed2d4 --- /dev/null +++ b/gui/src/window.ui @@ -0,0 +1,31 @@ + + + + + + True + False + Crimata Tech + center + 600 + 350 + center + + + True + True + 0 + 0 + + + + + + + + + diff --git a/networking/include/package_handler 2.h b/networking/include/package_handler 2.h new file mode 100644 index 0000000..68d58a9 --- /dev/null +++ b/networking/include/package_handler 2.h @@ -0,0 +1,18 @@ +#ifndef NETWORK +#define NETWORK + +#include + +int connect_socket ( char *server_ip , int port ); + +int socket_send ( char *message, int len ); + +int send_package ( char *data, int mes_len, int type ); + +int socket_recv ( char *response, int resp_len ); + +int recv_package ( char **data, int *mes_len ); + +int close_socket (); + +#endif diff --git a/networking/include/package_handler.h b/networking/include/package_handler.h new file mode 100644 index 0000000..68d58a9 --- /dev/null +++ b/networking/include/package_handler.h @@ -0,0 +1,18 @@ +#ifndef NETWORK +#define NETWORK + +#include + +int connect_socket ( char *server_ip , int port ); + +int socket_send ( char *message, int len ); + +int send_package ( char *data, int mes_len, int type ); + +int socket_recv ( char *response, int resp_len ); + +int recv_package ( char **data, int *mes_len ); + +int close_socket (); + +#endif diff --git a/networking/src/package_handler 2.c b/networking/src/package_handler 2.c new file mode 100644 index 0000000..c857dc6 --- /dev/null +++ b/networking/src/package_handler 2.c @@ -0,0 +1,231 @@ +/* +** Crimata Technologies - 2020 +** +** Package Handler: Landon Clark +** +** +** This file seeks to define quality-of-life functionality for client-side +** networking. This implementation allows us to hold our client socket +** information while abstracting away any handling of the socket data itself. +** +** The functions are as follows: +** +** *********************FUNCTIONS SEMANTICS************************* +** +** 1. @connect_socket ( host name, port number ) : This method allows us to +** initiate our client side socket with only an IP and port, thus abstracting +** the tedious and largely uniform aspects of the TCP socket away. +** +** 2. socket_send ( message, message length ) : This method allows us the +** ability to send a message across our pre-established socket needing only +** the message itself. +** +** 3. send_package ( package, package length ) : This method wraps up audio +** data as an audio package and sends it across the pre-established socket. +** +** 4. @@socket_recv ( message, message length ) : This method places 'message +** length' adjacent bytes of data received from the socket in the memory +** held by message. +** +** 5. recv_package ( package, package length ) : This method parses a received +** message to determine what type of message was sent, returns that value and +** stores the length of the data in the 'message length' variable and the +** actual data in ['message length' * sizeof('message type')] adjacent bytes +** starting at 'package'. The data returned in 'package' should be handled +** depending on the return value of this method as an audio message does not +** contain 'char' data while a text message does. +** +** 6. close_socket () : This method handles the closing of our pre-established +** socket. +** +** +** @ - This needs to be done before any other method - there are no +** protections on doing the others first, but they will not work as expected. +** +** @@ - Should be essentially a helper function as (4) handles packages which +** will likely be how we receive data from our sockets anyways. + */ + + +/* LIBRARIES */ +#include +#include +#include //strlen +#include +#include //inet_addr +#include + +/* HEADER FILE */ +#include "package_handler.h" + +/* ERROR macro to return if problems occur */ +#define ERROR -1 + +/* Packages will either be audio or text, an enum exposes this pretty well */ +enum _package_type { +AUDIO, +TEXT, +}; + + +/* This value will hold our socket information statically, allowing it to live + * outside of this files lifetime which may be essential */ +static int socket_desc; + + +/* Predefined header length as determined by Crimata Backend */ +size_t HEADER_LENGTH = 20; + + +int min_int (int a, int b) { + return (a < b) ? a : b; +} + +/* Boilerplate socket connection function */ +int connect_socket ( char *server_ip , int port ) { + + struct sockaddr_in server; + + //Create socket + socket_desc = socket(AF_INET , SOCK_STREAM , 0); + if (socket_desc == ERROR) + { + printf("Could not create socket"); + return ERROR; + } + + server.sin_addr.s_addr = inet_addr(server_ip); + server.sin_family = AF_INET; + server.sin_port = htons( port ); + + //Connect to remote server + if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) + { + puts("connect error"); + return ERROR; + } + + puts("Connected\n"); + + return 0; +} + + + +/* Boilerplate message sending function */ +int socket_send ( char *message, int len ) { + + if (send(socket_desc , message , len , 0) < 0) + { + puts("Send failed"); + return ERROR; + } + + return 0; +} + + +int send_package ( char *data, int mes_len, int type ) { + + char header[HEADER_LENGTH]; + char len[12]; + + switch(type) { + case 0: + strcpy(header, "aud-"); + break; + + case 1: + strcpy(header, "txt-"); + break; + + case 2: + strcpy(header, "con-"); + break; + } + + sprintf(len, "%d", mes_len); + + // makes eg. "aud-1532" + strcat(header, len); + + sprintf(header, "%-20s", header); + + // sending header and data separately + if ( socket_send(header, HEADER_LENGTH) < 0 ) { + puts("failed to send package header"); + return ERROR; + } + + if ( socket_send(data, mes_len) < 0) { + puts("failed to send package data"); + return ERROR; + } + + return 0; + +} + + +/* Boilerplate message receiving function */ +int socket_recv ( char *response, int resp_len ) { + + int bytes_left = resp_len; + int bytes_read = 0; + + int _read = 0; + + while (bytes_left > 0) { + _read = recv(socket_desc, &(response[bytes_read]), bytes_left, 0); + bytes_read += _read; + bytes_left -= _read; + } + + return 0; +} + + + +/* Boilerplate package parsing function */ +int recv_package ( char **data, int *mes_len ) { + + char header[HEADER_LENGTH+1]; + char *mes_type; + + if ( socket_recv(header, HEADER_LENGTH) < 0 ) { + puts("receiving packet failed"); + return ERROR; + } + + mes_type = strtok(header, "-"); + *mes_len = atoi((const char *) strtok(NULL, "\0")); + + *data = realloc(*data, (*mes_len+1)*sizeof(char)); + + if ( socket_recv(*data, *mes_len) < 0 ) { + puts("failed to receive data from package"); + return ERROR; + } + + if ( strcmp(mes_type, "aud") == 0 ) { + return AUDIO; + } + + if ( strcmp(mes_type, "txt") == 0 ) { + return TEXT; + } + + puts("could not find package type"); + return ERROR; + +} + + + +/* Boilerplate socket closing function */ +int close_socket () { + if (close(socket_desc) < 0) { + return ERROR; + } + return 0; +} diff --git a/networking/src/package_handler.c b/networking/src/package_handler.c new file mode 100644 index 0000000..c857dc6 --- /dev/null +++ b/networking/src/package_handler.c @@ -0,0 +1,231 @@ +/* +** Crimata Technologies - 2020 +** +** Package Handler: Landon Clark +** +** +** This file seeks to define quality-of-life functionality for client-side +** networking. This implementation allows us to hold our client socket +** information while abstracting away any handling of the socket data itself. +** +** The functions are as follows: +** +** *********************FUNCTIONS SEMANTICS************************* +** +** 1. @connect_socket ( host name, port number ) : This method allows us to +** initiate our client side socket with only an IP and port, thus abstracting +** the tedious and largely uniform aspects of the TCP socket away. +** +** 2. socket_send ( message, message length ) : This method allows us the +** ability to send a message across our pre-established socket needing only +** the message itself. +** +** 3. send_package ( package, package length ) : This method wraps up audio +** data as an audio package and sends it across the pre-established socket. +** +** 4. @@socket_recv ( message, message length ) : This method places 'message +** length' adjacent bytes of data received from the socket in the memory +** held by message. +** +** 5. recv_package ( package, package length ) : This method parses a received +** message to determine what type of message was sent, returns that value and +** stores the length of the data in the 'message length' variable and the +** actual data in ['message length' * sizeof('message type')] adjacent bytes +** starting at 'package'. The data returned in 'package' should be handled +** depending on the return value of this method as an audio message does not +** contain 'char' data while a text message does. +** +** 6. close_socket () : This method handles the closing of our pre-established +** socket. +** +** +** @ - This needs to be done before any other method - there are no +** protections on doing the others first, but they will not work as expected. +** +** @@ - Should be essentially a helper function as (4) handles packages which +** will likely be how we receive data from our sockets anyways. + */ + + +/* LIBRARIES */ +#include +#include +#include //strlen +#include +#include //inet_addr +#include + +/* HEADER FILE */ +#include "package_handler.h" + +/* ERROR macro to return if problems occur */ +#define ERROR -1 + +/* Packages will either be audio or text, an enum exposes this pretty well */ +enum _package_type { +AUDIO, +TEXT, +}; + + +/* This value will hold our socket information statically, allowing it to live + * outside of this files lifetime which may be essential */ +static int socket_desc; + + +/* Predefined header length as determined by Crimata Backend */ +size_t HEADER_LENGTH = 20; + + +int min_int (int a, int b) { + return (a < b) ? a : b; +} + +/* Boilerplate socket connection function */ +int connect_socket ( char *server_ip , int port ) { + + struct sockaddr_in server; + + //Create socket + socket_desc = socket(AF_INET , SOCK_STREAM , 0); + if (socket_desc == ERROR) + { + printf("Could not create socket"); + return ERROR; + } + + server.sin_addr.s_addr = inet_addr(server_ip); + server.sin_family = AF_INET; + server.sin_port = htons( port ); + + //Connect to remote server + if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) + { + puts("connect error"); + return ERROR; + } + + puts("Connected\n"); + + return 0; +} + + + +/* Boilerplate message sending function */ +int socket_send ( char *message, int len ) { + + if (send(socket_desc , message , len , 0) < 0) + { + puts("Send failed"); + return ERROR; + } + + return 0; +} + + +int send_package ( char *data, int mes_len, int type ) { + + char header[HEADER_LENGTH]; + char len[12]; + + switch(type) { + case 0: + strcpy(header, "aud-"); + break; + + case 1: + strcpy(header, "txt-"); + break; + + case 2: + strcpy(header, "con-"); + break; + } + + sprintf(len, "%d", mes_len); + + // makes eg. "aud-1532" + strcat(header, len); + + sprintf(header, "%-20s", header); + + // sending header and data separately + if ( socket_send(header, HEADER_LENGTH) < 0 ) { + puts("failed to send package header"); + return ERROR; + } + + if ( socket_send(data, mes_len) < 0) { + puts("failed to send package data"); + return ERROR; + } + + return 0; + +} + + +/* Boilerplate message receiving function */ +int socket_recv ( char *response, int resp_len ) { + + int bytes_left = resp_len; + int bytes_read = 0; + + int _read = 0; + + while (bytes_left > 0) { + _read = recv(socket_desc, &(response[bytes_read]), bytes_left, 0); + bytes_read += _read; + bytes_left -= _read; + } + + return 0; +} + + + +/* Boilerplate package parsing function */ +int recv_package ( char **data, int *mes_len ) { + + char header[HEADER_LENGTH+1]; + char *mes_type; + + if ( socket_recv(header, HEADER_LENGTH) < 0 ) { + puts("receiving packet failed"); + return ERROR; + } + + mes_type = strtok(header, "-"); + *mes_len = atoi((const char *) strtok(NULL, "\0")); + + *data = realloc(*data, (*mes_len+1)*sizeof(char)); + + if ( socket_recv(*data, *mes_len) < 0 ) { + puts("failed to receive data from package"); + return ERROR; + } + + if ( strcmp(mes_type, "aud") == 0 ) { + return AUDIO; + } + + if ( strcmp(mes_type, "txt") == 0 ) { + return TEXT; + } + + puts("could not find package type"); + return ERROR; + +} + + + +/* Boilerplate socket closing function */ +int close_socket () { + if (close(socket_desc) < 0) { + return ERROR; + } + return 0; +}