From 9e75ca73b288aa033496ec1574125cf73e0622a4 Mon Sep 17 00:00:00 2001 From: Andrew Gundersen Date: Mon, 27 Jul 2020 14:47:43 -0500 Subject: [PATCH] Initial commit --- .DS_Store | Bin 0 -> 6148 bytes audio/.DS_Store | Bin 0 -> 6148 bytes audio/include/.DS_Store | Bin 0 -> 6148 bytes audio/include/audio_handler.h | 14 + audio/include/frame_buffer.h | 24 ++ audio/src/.DS_Store | Bin 0 -> 6148 bytes audio/src/audio_handler.c | 348 ++++++++++++++++++ audio/src/frame_buffer.c | 120 ++++++ core/.DS_Store | Bin 0 -> 6148 bytes core/Info.plist | 37 ++ core/build/.DS_Store | Bin 0 -> 6148 bytes .../apps/Crimata.app/Contents/Info.plist | 37 ++ .../apps/Crimata.app/Contents/MacOS/Crimata | Bin 0 -> 37176 bytes .../Crimata.app/Contents/Resources/ICON.icns | Bin 0 -> 41619 bytes core/build/objects/src/main.o | Bin 0 -> 4272 bytes core/include/main.h | 31 ++ core/makefile | 88 +++++ core/resources/.DS_Store | Bin 0 -> 6148 bytes core/resources/ICON.icns | Bin 0 -> 41619 bytes core/src/main.c | 172 +++++++++ gui/include/anims.h | 28 ++ gui/include/eases.h | 46 +++ gui/include/gtk-cairo_api.h | 61 +++ gui/src/anims.c | 81 ++++ gui/src/bounce_anim.c | 33 ++ gui/src/eases.c | 128 +++++++ gui/src/gtk-cairo_api.c | 60 +++ gui/src/print_anim.c | 18 + gui/src/recording_anim.c | 228 ++++++++++++ gui/src/standby_anim.c | 134 +++++++ gui/src/start_up_anim.c | 195 ++++++++++ gui/src/window 2.css | 55 +++ gui/src/window 2.ui | 31 ++ gui/src/window.css | 55 +++ gui/src/window.ui | 31 ++ networking/include/package_handler 2.h | 18 + networking/include/package_handler.h | 18 + networking/src/package_handler 2.c | 231 ++++++++++++ networking/src/package_handler.c | 231 ++++++++++++ 39 files changed, 2553 insertions(+) create mode 100644 .DS_Store create mode 100644 audio/.DS_Store create mode 100644 audio/include/.DS_Store create mode 100644 audio/include/audio_handler.h create mode 100644 audio/include/frame_buffer.h create mode 100644 audio/src/.DS_Store create mode 100644 audio/src/audio_handler.c create mode 100644 audio/src/frame_buffer.c create mode 100644 core/.DS_Store create mode 100644 core/Info.plist create mode 100644 core/build/.DS_Store create mode 100644 core/build/apps/Crimata.app/Contents/Info.plist create mode 100755 core/build/apps/Crimata.app/Contents/MacOS/Crimata create mode 100644 core/build/apps/Crimata.app/Contents/Resources/ICON.icns create mode 100644 core/build/objects/src/main.o create mode 100644 core/include/main.h create mode 100644 core/makefile create mode 100644 core/resources/.DS_Store create mode 100644 core/resources/ICON.icns create mode 100644 core/src/main.c create mode 100644 gui/include/anims.h create mode 100644 gui/include/eases.h create mode 100644 gui/include/gtk-cairo_api.h create mode 100644 gui/src/anims.c create mode 100644 gui/src/bounce_anim.c create mode 100644 gui/src/eases.c create mode 100644 gui/src/gtk-cairo_api.c create mode 100644 gui/src/print_anim.c create mode 100644 gui/src/recording_anim.c create mode 100644 gui/src/standby_anim.c create mode 100644 gui/src/start_up_anim.c create mode 100644 gui/src/window 2.css create mode 100644 gui/src/window 2.ui create mode 100644 gui/src/window.css create mode 100644 gui/src/window.ui create mode 100644 networking/include/package_handler 2.h create mode 100644 networking/include/package_handler.h create mode 100644 networking/src/package_handler 2.c create mode 100644 networking/src/package_handler.c diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e8ccd8cdeb33e4ec935eb7339483a49147a27283 GIT binary patch literal 6148 zcmeH~&2G~`5XWa57wjTL4oEGxy&!SOAx_)06+*H?IrN5P1P4H^U0bO|@K%X~1t!QB z9s+m*9tB>2$AOvIC2|satN7B6H2dGL+0Fjs-OU1k7)%o%pbY>nI;pdP$uErB$yaR6 z)KH_~G1SpWoyH>-uk^JQmVhPj_Yt7IyABadA%P5<`R#o-;7kNOaP`Xswlx z4ovt7fa+j18|LXwq!?*5Fj{NH7BrzlQFW*=PYj{Mv0j>ffzev44hLZ#AHvKm%nL>E z>^Q#U?jQoKHnjvSfkgr>U2N0+|KQ^H|6-6WSpt^8e?>qzhvDG>mt^kNmBrCrE74ET q$*5ngl|wKw*D+`4D&9h8!!}77)WB%16>HG!9|6W-6HDN?68Hfin!}+0 literal 0 HcmV?d00001 diff --git a/audio/.DS_Store b/audio/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5f15c9f727df36e89b515512b88503b2828abf71 GIT binary patch literal 6148 zcmeHK%}(1u5S}Fg>>@-CNaW~CB@Q_x1@R+P$qI1Dt&%HxXe+Se2rL;dik(Ifg8Ulr z20RK+z~j)F*(oAPdqoB9NVDJU&djd&lRfJN0M#DEEr1#TIH-ix57_)g$WJ;Wb274k zsCbS6#L(-cx*Mf4(RRQxU>W$|7@%)=5faFt3n{$#eszQ%Dhqq4>rX6b%|u7T{31#@ z`XRbVle}m)AJtr?I=`^!xUSWRJvmHi9NpE;u zTiw@L5hhu$&&f&L!;l}}k}TH4wjN}0&hrGu!3!PN>Z8%8)^5Yw+}Ip9ywT1!*;~8g zvFogTY<=#WoL^pzuE#gGyb(}Xv{+UI|G*s@Q>6R~`dO;8Q}j?)1#;*^*Oa&240f^st+D2p)Xh(9W8_D6teu#IKlRT=mV$e63} literal 0 HcmV?d00001 diff --git a/audio/include/.DS_Store b/audio/include/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..052ae883b3db5aa58cdad91573c011faf70d8907 GIT binary patch literal 6148 zcmeHK%Sr=55Ukc50)p<*<9vbO9}FQLJo^DLYYY)KE(p2rpKI0hD6a0olZ4P6W~!%O zGh2{(0If5_j?tvGUvb3;uJ{Gt*Wck8 zZ@9tkzU!abu5TIHy#El|F0p0Y8hggC(6Z8&zrW=kBO|e27-Z8XMkt6WPafo zeJ~IV1Os0TXn#m-!d!81EL#UVU7zKj2_@9^mJp_jx#HkR55+u{=&4qp80P7;ry7?l z4vwAJiBo%!?eVr6#PQwfJ<$LNEBU|`I^xepti|9AY$Og{3*DKQELf`Jobz-G&b z<$@no&(>d`r?WP(J+es@H%OsSA6)`C&^~fqobFH38J8;#j;bPiNhiidpb!#WFz^oy Fd;kr9K7{}P literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..653db42f5d6ebaae434e3ae291aebf880c3a9d8f GIT binary patch literal 6148 zcmeHK%Sr=55Ukc50)p<*<9vbO9}FQLJo^DLn-~-}E(p2rpKI0hD6a0olZ4P6W~!%O zGh2{(0Ieb;Z=u5TIHy#El|F0o_W8hggC(6Z8wzrW=kBO|e27-Z8XMkt6WPafo zeJ~IV1Os0TXn#m-!d!81EL#UVU7zKj2_@9^mJp_jx#HkR55+u{=&4qp80P7;ry7?l z4vwAJiBo%!?eVr6#PQwfJ<$LNEBU|`I^xepti|9AY$Og{3*DKQELf`Jobz-G&b z<$@no&(>d`r?WP(J+es@H%OsSA6)`C&^~fqobFH38J8;#j;bPiNhiidpb!#WFz^oy Fd;lsoK6?NF literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e827c27ff5b1d1656501cf4c430864f3a7298f11 GIT binary patch literal 6148 zcmeHKL2uJA6n=)j7Hunm@DD#m&d&ad#esLb?_;Hpczhggm5=ZmX z-i_ldE&Mo*W|AC-5mKH%jngojkFqiibNL-lb;yBEd$$&g;c&n2+&ef}_MOFWFu31$ z4)+h2OWWGJJ$x`eyLfxKc(;82QO<${PAh5GHU5H62x@bD<5z=Wa#6;30I*hjRHo2rUD&R?eYEp@Xzo6Cdt%{0!D$2Qb2T1yps{8r0>?1 x$?;w5z|Y`poL4E76cqY8mH}VIH{sII=WqaYHC77I0yBREqztAo3j9+Aegc}J + + + + + 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 0000000000000000000000000000000000000000..4eb442f63fe4219aafa27ba21742eb12775ff900 GIT binary patch literal 6148 zcmeHK!EVzq82;UMGyx%cK-#$Fg2bVRW@zJpCPif&c0&pp2S8i0R9zs%Rg)5Jr6`xZ z#P$Td3NOHe@Br-lZBx;VUJ(NSmHpZO|NXZ7xw2y-62n>I6ZMG5L1ApUsD5J{=W@eZ z#?u7~9^)XBCvhfY^m#yQf4>5}cO8mof?v_tzo&>j`k_oMWXX%GVq*6BYdp!)q8tpq ziB`L_ajWaNuH)Wy--f5M3d^XR6~k!yntLyz@-#N>mr`cSb`&3`qw1)4|C!9oD9uMx zm7FFcP~N;s^F&rdIm?qmjT7pCn&_zaU_ST#?Y{STXJ^s(=Kf&tr0?x+?=BXuv-!~9 zJ2?Juc0T{K_U+AM-b$(2NpFNv>)nE)e}yqc)Af zOdG7t2J1HEG@_C!k~F3F7&oVJBjRp5^*a1D8`jfwyO literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..70a37526850a62fbf9a09c54c414ea79a8a08936 GIT binary patch literal 37176 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 Myc?A~xvVw*A2ey5H2?qr literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..73e1e87c9601db7d3fd03af6c6752fd16cd45c24 GIT binary patch literal 41619 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 VaN$&nDDbLVs5qtQ264DJ?7wi)M(_Xt literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1375bbf43af2db81bd622eb8c8cc6d4b5b9228c8 GIT binary patch literal 6148 zcmeHKJ5EC}5S)b+K{T(V^a+r-ffYiL1_~~K5*nn46e#_xI2SjA*^dz61&M|R%}VRB z*E_a6#oM<4Y_Wg50tNtcx+7kFn40gqPwb>3Mx^tMOFUtNXS}SolkDRG=bmAOYuw@X zD}T3HZ+C0%4)5RW_(6(F0VyB_q<|EV0>4tgYcFkno~Tg@NC7GEsepeU8r`uMj*0Q< z;1DeUamH{M=g~_Ln+J%!a7<)`W=SO`)vCp?q%+QF3hXS_u^ ztS4%e0#e{mfzwA n4pEMYQI5Ira(odn)^2mlQ5pPv7m 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 + +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; +}