← index
acidcamGL/source/acidcamGL-Visual.Studio/acidcam_window.hpp
Source: acidcamGL/source/acidcamGL-Visual.Studio/acidcam_window.hpp
#include"gl_window.hpp"
#include"gl_shader.hpp"
#include"glm/glm.hpp"
#include"glm/gtc/type_ptr.hpp"
#include"glm/gtc/matrix_transform.hpp"
#ifdef _WIN32
#include"getopt.h"
#include"ac.h"
#else
#include"acidcam/ac.h"
#include<unistd.h>
#endif
#include<vector>
#include<sstream>
#include<string>
#include<fstream>
#include<iomanip>
#include<iostream>
#include<chrono>
#include<algorithm>
#include"keymap.hpp"
#include"ipc_client.hpp"
#define version_info "v1.0.003"
#ifdef SYPHON_SERVER
#include"syphon.h"
#endif
#include<unordered_map>
#ifdef SYPHON_SERVER
extern void ScreenGrabRect(int x, int y, int w, int h, cv::Mat &frame);
#endif
#include"ffmpeg_write.h"
namespace acidcam {

extern cv::VideoCapture cap;
extern int redir;
extern int syphon_enabled;

class AcidCam_Window : public glWindow {
    static constexpr int numVAOs = 1;
    static constexpr int numVBOs = 2;
    GLuint vao[numVAOs];
    GLuint vbo[numVBOs];
    ShaderProgram program;
    GLuint mv_loc, proj_loc;
    int width, height;
    float aspect;
    glm::mat4 p_mat, v_mat, m_mat, mv_mat;
    GLuint texture;
    float color_alpha_r, color_alpha_g, color_alpha_b;
    std::vector<ShaderProgram> shaders;
    cv::VideoWriter writer;
    bool writer_set = false;
    int shader_index;
    bool print_text;
    bool debug;
    float alpha;
    bool ac_on;
    glm::vec4 optx, random_var;
    KeyMap mapped_keys;
    float movement_rate;
    bool take_snapshot;
    std::string snapshot_prefix;
    bool restore_black;
    bool list_enabled;
    std::vector<int> var_list;
    int var_index;
    bool repeat;
    int color_map;
    int blend_index;
    int sx, sy;
    bool video_mode;
    int fps;
    int screen_x, screen_y;
    bool playback_mode;
    bool playback_sort;
    int p_timeout;
    bool rand_timeout;
    bool paused;
    bool stereo_;
    int stored_position, stored_var_position;
    bool rand_shader;
    bool shader_list_enabled;
    FILE *fptr;
    std::vector<int> shader_list;
    std::unordered_map<std::string, int> shader_map;
public:
    
    AcidCam_Window() = default;
    AcidCam_Window(const AcidCam_Window &) = delete;
    AcidCam_Window &operator=(const AcidCam_Window &) = delete;
    
    void setVideoMode(bool b, int f) {
        video_mode = b;
        fps = f;
    }
    
    void setFilePointer(FILE *fptr, int w, int h) {
        this->fptr = fptr;
        writer_w = w;
        writer_h = h;
    }
    
    virtual void init() override {
        shader_list_enabled = false;
        fptr = 0;
        rand_shader = false;
        stored_position = 0;
        stored_var_position = 0;
        stereo_ = false;
        paused = false;
        rand_timeout = false;
        video_mode = false;
        p_timeout = 1;
        blend_index = 0;
        color_map = -1;
        var_index = 0;
        list_enabled = false;
        restore_black = false;
        snapshot_prefix="AcidCamGL_Snapshot";
        take_snapshot = false;
        optx = glm::vec4(0.5,0.5,0.5,0.5);
        index = 0;
        movement_rate = 0.07f;
        ac_on = false;
        alpha = 0.1f;
        debug = false;
        print_text = false;
        shader_index = 0;
        color_alpha_r = 0.1;
        color_alpha_g = 0.2;
        color_alpha_b = 0.3;
        repeat = false;
        GLfloat vertices[] = {
            -1.0f, -1.0f, 1.0f, // front face
            1.0f, 1.0f, 1.0f,
            -1.0f, 1.0f, 1.0f,
            
            -1.0f, -1.0f, 1.0f,
            1.0f, -1.0f, 1.0f,
            1.0f, 1.0f, 1.0f
        };
        
        GLfloat texCoords[] = {
            0, 0, // front
            1, 1,
            0, 1,
            
            0, 0,
            1, 0,
            1, 1,
        };
        playback_mode = false;
        glGenVertexArrays(1, vao);
        glBindVertexArray(vao[0]);
        glGenBuffers(numVBOs, vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW);
        
        glfwGetFramebufferSize(win(), &width, &height);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        aspect = (float)width/(float)height;
        p_mat = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f);
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        cv::Mat frame;
        if(!cap.isOpened()) {
            return;
        } else {
            cap.read(frame);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frame.cols, frame.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, frame.ptr());
            sx = frame.cols;
            sy = frame.rows;
        }
    }
    
    int index;
    
    void setShader(int index) {
        program = shaders[index];
        if(debug) {
            std::cout << "acidcam: Shader Program Loaded: " << program.name() << "\n";
        }
    }
    
    void sortPlaylist() {
        unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
        std::shuffle(var_list.begin(), var_list.end(), std::default_random_engine(seed));
    }
    
    
    void setPlaybackMode(bool v, int timeout = 0, int beat = 0, int bpm = 0, bool  s = false) {
        
        if(p_timeout == 0) {
            std::cout << "acidcam: Error you must set playback timeout with -N\n";
            acidcam::updateError();
        }
        playback_mode = v;
        if(s)
            std::sort(var_list.begin(), var_list.end());
        if(bpm != 0) {
            p_timeout = beat;
            std::cout <<"acidcam: Beats per minute: " << bpm << " frames per beat: " << p_timeout << "\n";
        }
        else
            p_timeout = timeout;
    }
    
    GLuint material = 0;
    bool material_on = false;
    float img_cols = 0, img_rows = 0;
    
    void genMaterial(std::string s) {
        glGenTextures(1, &material);
        glBindTexture(GL_TEXTURE_2D, material);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        
        cv::Mat frame;
        frame = cv::imread(s);
        if(frame.empty()) {
            std::cout << "acidcam: Error could not load texture: " << s << "\n";
            acidcam::updateError();
        }
        
        cv::Mat flipped;
        cv::flip(frame, flipped, 0);
        cv::cvtColor(flipped, frame, cv::COLOR_BGR2RGB);
        
        img_cols = frame.cols;
        img_rows = frame.rows;
        
        
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frame.cols, frame.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, frame.ptr());
        
        std::cout << "acidcam: Successfully loaded material: " << s << "\n";
        material_on = true;
    }
    
    void setPrintText(bool b) {
        print_text = b;
    }
    
    void setPrefix(const std::string &s) {
        snapshot_prefix = s;
    }
    
    void setRestoreBlack(bool &b) {
        restore_black = b;
    }
    
    void loadKeys(const std::string &n) {
        mapped_keys.load(n);
    }
    
    
    int find_solo(const std::string &s) {
        for(int i = 0; i < ac::solo_filter.size(); ++i) {
            if(ac::solo_filter[i] == s)
                return i;
        }
        return -1;
    }
    
    void loadList(const std::string &l, bool s = false) {
        std::fstream file;
        file.open(l, std::ios::in);
        if(!file.is_open()) {
            std::cout << "acidcam: Error could not open playlist: " << l << "\n";
            acidcam::updateError();
        }
        while(!file.eof()) {
            std::string s;
            std::getline(file, s);
            if(file) {
                if(s.length()>0) {
                    int value = find_solo(s);
                    if(value == -1) {
                        std::cout << "acidcam: Error could not find for playlist string: " << s << "\n";
                        //quit();
                        //return;
                    } else
                        var_list.push_back(value);
                }
            }
        }
        file.close();
        std::cout << "acidcam: Playlist loaded [" << var_list.size() << "] items...\n";
        if(s == true) {
            sortPlaylist();
            std::cout << "acidcam: Playlist Shuffled...\n";
        }
    }
    
    bool screen_mode = false;
    
    void enableScreenMode(bool b, int x, int y, int w, int h) {
        screen_mode = b;
        sx = w;
        sy = h;
        screen_x = x;
        screen_y = y;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        cv::Mat frame(h, w, CV_8UC3);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frame.cols, frame.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, frame.ptr());
    }
    
    void takeSnapshot() {
        static int index = 0;
        ++index;
        cv::Mat flipped;
        readFrame(flipped);
        time_t t = time(0);
#ifdef _WIN32
        struct tm mm;
        localtime_s(&mm, &t);
        struct tm* m = &mm;
#else
        struct tm *m;
        m = localtime(&t);
#endif
        std::ostringstream time_stream;
        time_stream << snapshot_prefix << "-" << (m->tm_year + 1900) << "." << std::setw(2) << std::setfill('0') << (m->tm_mon + 1) << "." << std::setw(2) << std::setfill('0') << m->tm_mday << "_" << std::setw(2) << std::setfill('0') << m->tm_hour << "." << std::setw(2) << std::setfill('0') << m->tm_min << "." << std::setw(2) << std::setfill('0') << m->tm_sec <<  "_" << flipped.cols << "x" << flipped.rows << "x" << index << ".png";
        cv::imwrite(time_stream.str(), flipped);
        std::cout << "acidcam: Wrote: " << time_stream.str() << "\n";
        if(redir == 1) {
#ifdef SYPHON_SERVER
            if(redirect != 0) sendString(redirect->getString());
#endif
        }
    }
    
    void readFrame(cv::Mat &frame) {
        cv::Mat img;
        img.create(window_height, window_width,CV_8UC3);
        glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4);
        glPixelStorei(GL_PACK_ROW_LENGTH, (int)img.step/img.elemSize());
        glReadPixels(0, 0, img.cols, img.rows, GL_RGB, GL_UNSIGNED_BYTE, img.data);
        cv::Mat flipped;
        cv::flip(img, flipped, 0);
        cv::cvtColor(flipped, frame, cv::COLOR_RGB2BGR);
    }
    
    int writer_w = 0, writer_h = 0;
    
    void setWriter(cv::VideoWriter w, int writer_ww, int writer_hh) {
        writer = w;
        writer_set = true;
        writer_w = writer_ww;
        writer_h = writer_hh;
    }
    
    void write(cv::Mat &frame) {
        if(fptr != 0) {
            cv::Mat reimage;
            cv::resize(frame, reimage, cv::Size(writer_w, writer_h));
            write_ffmpeg(fptr, frame);
        } else {
            if(window_width != writer_w && window_height != writer_h) {
                cv::Mat re;
                cv::resize(frame, re, cv::Size(writer_w, writer_h));
                writer.write(frame);
            }
            else
                writer.write(frame);
        }
#ifdef SYPHON_SERVER
        if(redirect != 0) {
            static int frame = 1;
            int total = cap.get(cv::CAP_PROP_FRAME_COUNT);
            if((frame%fps*20)==0 && fps > 0) {
                std::ostringstream stream;
                stream << "acidcam: wrote frame: " << frame << "/" << total << "/" << (frame/fps) << " seconds...\n";
                sendString(stream.str());
            }
            ++frame;
        }
#endif
    }
    
    void writeFrame() {
        cv::Mat frame;
        readFrame(frame);
        write(frame);
    }
    
    void setStereo(bool b) {
        stereo_ = b;
        if(b == true)
            std::cout << "acidcam: Stereo mode enabled...\n";
    }
    
    virtual void update(double timeval) override {
        if(paused)
            return;
        std::chrono::time_point<std::chrono::system_clock> now =
        std::chrono::system_clock::now();
        
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClearDepth(1.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        if(rand_shader == true)
            program = shaders[rand()%(shaders.size()-1)];
        
        program.useProgram();
        mv_loc = glGetUniformLocation(program.id(), "mv_matrix");
        proj_loc = glGetUniformLocation(program.id(),"proj_matrix");
        GLuint samp = glGetUniformLocation(program.id(),"samp");
        GLuint mat_samp = glGetUniformLocation(program.id(),"mat_samp");
        GLuint calpha_r = glGetUniformLocation(program.id(),"value_alpha_r");
        GLuint calpha_g = glGetUniformLocation(program.id(),"value_alpha_g");
        GLuint calpha_b = glGetUniformLocation(program.id(),"value_alpha_b");
        GLuint c_index = glGetUniformLocation(program.id(),"index_value");
        GLuint c_tf = glGetUniformLocation(program.id(),"time_f");
        GLuint alpha_pos = glGetUniformLocation(program.id(), "alpha_value");
        GLuint optx_pos = glGetUniformLocation(program.id(), "optx");
        GLuint rand_pos = glGetUniformLocation(program.id(), "random_var");
        GLuint restore_blackx = glGetUniformLocation(program.id(), "restore_black");
        GLuint inc_value_pos = glGetUniformLocation(program.id(), "inc_value");
        GLuint inc_value_posx = glGetUniformLocation(program.id(), "inc_valuex");
        
        
        GLuint material_size;
        material_size = glGetUniformLocation(program.id(), "mat_size");
        
        v_mat = glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, 0));
        m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(0,0,0));
        
        mv_mat = v_mat * m_mat;
        
#ifdef SYPHON_SERVER
        if(syphon_enabled) {
            syphon_bind(window_width, window_height);
        }
#endif
        
        glUniformMatrix4fv(mv_loc, 1, GL_FALSE, glm::value_ptr(mv_mat));
        glUniformMatrix4fv(proj_loc, 1, GL_FALSE, glm::value_ptr(p_mat));
        
        glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
        glVertexAttribPointer(0,3,GL_FLOAT, GL_FALSE,0,0);
        glEnableVertexAttribArray(0);
        
        glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
        glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,0,0);
        glEnableVertexAttribArray(1);
        
        cv::Mat frame;
        
        
        if(screen_mode == false) {
            if(!cap.read(frame)) {
                if(repeat == true && repeat_filename.length()>0) {
                    cap.open(repeat_filename);
                    if(cap.isOpened()) {
                        cap.read(frame);
                        std::cout << "acidcam: video loop...\n";
                    } else {
                        std::cout << "acidcam: Capture device closed exiting...\n";
                        quit();
                        return;
                    }
                } else {
                    std::cout << "acidcam: Capture device closed exiting...\n";
                    quit();
                    return;
                }
            }
        } else {
#ifdef SYPHON_SERVER
            cv::Mat cvMat, temp_frame;
            ScreenGrabRect(screen_x, screen_y, window_width, window_height, cvMat);
            cv::cvtColor(cvMat, temp_frame, cv::COLOR_RGBA2BGR);
            cv::resize(temp_frame, cvMat, cv::Size(sx, sy));
            frame = cvMat;
#endif
        }
        
        if(playback_mode && list_enabled) {
            static int playback_index = 0;
            static int frame_time = 0;
            static int current_timeout = p_timeout;
            ++frame_time;
            if(p_timeout == 0 || frame_time > current_timeout) {
                ++playback_index;
                if(rand_timeout && p_timeout)
                    current_timeout = 1+rand()%p_timeout;
            }
            if(frame_time > p_timeout)
                frame_time = 0;
            if(playback_index > var_list.size()) {
                playback_index = 0;
                sortPlaylist();
            }
            index = var_list[playback_index];
        }
    
        if(shader_index == 0 || ac_on == true) {
            if(index >= 0 && index < ac::solo_filter.size()) {
                cv::Mat orig;
                
                if(blend_index > 0) {
                    orig = frame.clone();
                }
                
                CallCustom(ac::solo_filter[index], frame);
                
                if(blend_index > 0 && blend_index/10 > 0) {
                    cv::Mat copyf;
                    double per = 1.0/(blend_index/10);
                    ac::AlphaBlendDouble(frame, orig, copyf, per, 1.0-per);
                    frame = copyf.clone();
                }
                
                if(color_map != -1) {
                    cv::Mat output_f1 = frame.clone();
                    cv::applyColorMap(output_f1, frame, color_map);
                }
                
                if(restore_black == true) {
                    ac::CallFilter("RestoreBlack", frame);
                    if(color_map != -1) {
                        cv::Mat output_f1 = frame.clone();
                        cv::applyColorMap(output_f1, frame, color_map);
                    }
                }
                if(print_text == true) {
                    std::ostringstream stream;
                    stream << ac::solo_filter[index];
                    cv::putText(frame, stream.str(),cv::Point(40, 40),cv::FONT_HERSHEY_DUPLEX,1.0,CV_RGB(255, 255, 255), 2);
                }
            }
        }
        
        cv::flip(frame, frame, 0);
        if(stereo_)
            ac::Stereo(frame);
        
        
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture);
        
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame.cols, frame.rows, GL_BGR, GL_UNSIGNED_BYTE, frame.ptr());
        
        if(material_on) {
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, material);
        }
        color_alpha_r += rand()%100 * 0.01f;
        color_alpha_g += rand()%100 * 0.01f;
        color_alpha_b += rand()%100 * 0.01f;
        
        if(color_alpha_r > 1.5f)
            color_alpha_r = 0.1f;
        if(color_alpha_g > 1.5f)
            color_alpha_g = 0.1f;
        if(color_alpha_b > 1.5f)
            color_alpha_b = 0.1f;
        static bool idir = true;
        if(idir == true) {
            alpha += 0.1f;
            if(alpha >= 6.0)
                idir = false;
        } else {
            alpha -= 0.1f;
            if(alpha <= 1.0f) {
                idir = true;
            }
            
        }
        random_var = glm::vec4(rand()%255, rand()%255, rand()%255, rand()%255);
        
        static glm::vec4 inc_value(rand()%255, rand()%255, rand()%255, 1);
        
        static int inc_dir = 1;
        
        if(inc_dir == 1) {
            for(int i = 0; i < 3; ++i) {
                ++inc_value[i];
                if(inc_value[i] >= 255)
                    inc_dir = 0;
            }
        } else {
            for(int i = 0; i < 3; ++i) {
                --inc_value[i];
                if(inc_value[i] <= 1) {
                    inc_dir = 1;
                    inc_value = glm::vec4(rand()%255, rand()%255, rand()%255, 1);
                }
            }
        }
        
        static glm::vec4 inc_valuex(rand()%255, rand()%255, rand()%255, 1);
        
        static int inc_dirx = 1;
        
        if(inc_dirx == 1) {
            for(int i = 0; i < 3; ++i) {
                ++inc_valuex[i];
                if(inc_valuex[i] >= 255)
                    inc_dirx = 0;
            }
        } else {
            for(int i = 0; i < 3; ++i) {
                --inc_valuex[i];
                if(inc_valuex[i] <= 1) {
                    inc_dirx = 1;
                }
            }
        }
        
        glUniform1i(samp, 0);
        glUniform1i(mat_samp, 1);
        glUniform1f(c_index, (float)index);
        glUniform1f(c_tf, timeval);
        glUniform4fv(inc_value_pos, 1, glm::value_ptr(inc_value));
        glUniform4fv(inc_value_posx, 1, glm::value_ptr(inc_valuex));
        
        glUniform1f(calpha_r, color_alpha_r);
        glUniform1f(calpha_g, color_alpha_g);
        glUniform1f(calpha_b, color_alpha_b);
        glUniform1f(alpha_pos, alpha);
        glUniform4fv(optx_pos, 1, glm::value_ptr(optx));
        glUniform4fv(rand_pos, 1, glm::value_ptr(random_var));
        glUniform1f(restore_blackx, ((restore_black == true) ? 1.0 : 0.0));
        GLint loc = glGetUniformLocation(program.id(), "iResolution");
        glUniform2f(loc, width, height);
        glUniform2f(material_size, img_cols, img_rows);

        glDrawArrays(GL_TRIANGLES,0,6);
#ifdef SYPHON_SERVER
        int tex = syphon_pushTexture(texture);
        glBindTexture(GL_TEXTURE_RECTANGLE_EXT, tex);
        glDrawArrays(GL_TRIANGLES, 0, 6);
#endif

        if(shader_list_enabled) {
            for(int i = 0; i < shader_list.size(); ++i) {
                // not sure how to do this
            }
        }
        
        if(take_snapshot == true) {
            takeSnapshot();
            take_snapshot = false;
        }
        if(writer_set == true)
            writeFrame();
        
        if(video_mode && fps != 0) {
            std::chrono::time_point<std::chrono::system_clock> nowx =
            std::chrono::system_clock::now();
            auto m = std::chrono::duration_cast<std::chrono::milliseconds>(nowx-now).count();
            if(fps > 0) {
                int fps_mil = 1000/fps;
                if(m < fps_mil)
                    std::this_thread::sleep_for(std::chrono::milliseconds(fps_mil-m-1));
            }
        }
    }
    
    void setDebug(bool d) {
        debug = d;
    }
    
    std::string repeat_filename;
    
    void setRepeat(std::string fn, bool r) {
        repeat = r;
        repeat_filename = fn;
    }
    
    void setColorMap(int map) {
        color_map = map;
        if(map != -1) {
            std::cout << "acidccam: Set color map: " << map << "\n";
        }
    }
    
    std::string input_string;
    
    void typeKey(unsigned int key) {
        if(key >= '0' && key <= '9')
            input_string += static_cast<char>(key);
    }
    
    void setFilterIndex(const int &i) {
        index = i;
    }
    
    std::fstream *stdout_file;
    
    void setFile(std::fstream  *fout) {
        stdout_file = fout;
    }
    
    void keypress(int key, int scancode, int action, int mode) {
        if(key == GLFW_KEY_ESCAPE) {
            quit();
            return;
        }
        
        if(action == GLFW_RELEASE) {
            int f = 0, s = 0;
            if(mapped_keys.checkKey(key, f, s)) {
                index = f;
                std::cout << "acidcam: Filter: " << ac::solo_filter[index] << "\n";
                setShader(s);
                return;
            }
            
            switch(key) {
                case GLFW_KEY_COMMA:
                    
                    if(color_map == 0) {
                        std::cout << "acidcam: Color map disabled...\n";
                        color_map = -1;
                        return;
                    }
                    
                    if(color_map > 0) {
                        color_map--;
                        
                        std::cout << "acidcam: Color Map Decreased to: " << color_map << "\n";
                    }
                    break;
                case GLFW_KEY_PERIOD:
                    if(color_map < 19) {
                        color_map++;
                        std::cout << "acidcam: Color map increased to: " << color_map << "...\n";
                    }
                    break;
                case GLFW_KEY_L:
                    if(var_list.size()>0) {
                        static int offset = 0;
                        list_enabled = !list_enabled;
                        if(list_enabled == true && var_list[var_index] >= 0 && var_list[var_index] < ac::solo_filter.size()) {
                            offset = index;
                            index = var_list[var_index];
                            std::cout << "acidcam: filter mode [list] enabled...\n";
                            
                        } else if(list_enabled == false) {
                            index = offset;
                            std::cout << "acidcam: filter mode [normal] restored...\n";
                        }
                    }
                    break;
                case GLFW_KEY_N:
                    if(debug)
                        std::cout << "acidcam: filter index set to end of list...\n";
                    if(list_enabled == false)
                        index = ac::solo_filter.size()-3;
                    break;
                case GLFW_KEY_P:
                    if(debug)
                        std::cout << "acidcam: filter index reset...\n";
                    
                    if(list_enabled == false)
                        index = 0;
                    break;
                case GLFW_KEY_K:
                    if(index+25 < ac::solo_filter.size()-3)
                        index += 25;
                    break;
                case GLFW_KEY_J:
                    if(index-25 > 0)
                        index -= 25;
                    break;
                case GLFW_KEY_Z:
                    take_snapshot = true;
                    break;
                case GLFW_KEY_ENTER:
                case GLFW_KEY_F: {
                    int val = atoi(input_string.c_str());
                    if(val >= 0 && val <= ac::solo_filter.size()-3) {
                        index = val;
                        input_string = "";
                        std::cout << "acidcam: Filter: " << ac::solo_filter[index] << "\n";
                    }
                }
                    break;
                case GLFW_KEY_S: {
                    int val = atoi(input_string.c_str());
                    if(val >= 0 && val <= shaders.size()-1) {
                        input_string = "";
                        setShader(val);
                    }
                }
                    break;
                case GLFW_KEY_C: {
                    input_string = "";
                    break;
                }
                case GLFW_KEY_SPACE:
                    ac_on = !ac_on;
                    if(ac_on)
                        std::cout << "acidcam: filters enabled...\n";
                    else
                        std::cout << "acidcam: filters disabled...\n";
                    break;
                case GLFW_KEY_LEFT:
                    if(list_enabled == false) {
                        if(index > 0) {
                            --index;
                            if(debug) {
                                std::cout << "acidcam: Filter Index: " << index << " - " << ac::solo_filter[index] << "\n";
                            }
                        }
                    } else {
                        if(var_index > 0)
                            var_index -= 1;
                        
                        if(var_list[var_index] > 0 && var_list[var_index] < ac::solo_filter.size()) {
                            index = var_list[var_index];
                            if(debug) {
                                std::cout << "acidcam: Filter Index: " << index << " - " << ac::solo_filter[index] << "\n";
                            }
                        }
                    }
                    break;
                case GLFW_KEY_RIGHT:
                    if(list_enabled == false) {
                        if(index < ac::solo_filter.size()) {
                            ++index;
                            if(debug) {
                                std::cout << "acidcam: Filter Index: " << index << " - " << ac::solo_filter[index] << "\n";
                            }
                        }
                    } else {
                        if(var_index < var_list.size())
                            var_index += 1;
                        if(var_list[var_index] > 0 && var_list[var_index] < ac::solo_filter.size()) {
                            index = var_list[var_index];
                            if(debug) {
                                std::cout << "acidcam: Filter Index: " << index << " - " << ac::solo_filter[index] << "\n";
                            }
                        }
                    }
                    break;
                case GLFW_KEY_M:
                    rand_timeout = !rand_timeout;
                    if(rand_timeout)
                        std::cout << "acidcam: Rand playlist timeout enabled\n";
                    else
                        std::cout << "acidcam: Rand playlist timeout disabled\n";
                    break;
                case GLFW_KEY_MINUS:
                    if(blend_index > 0) {
                        blend_index -= 10;
                        std::cout << "acidcam: Blend Index Set to: " << blend_index << ".\n";
                    }
                    break;
                case GLFW_KEY_EQUAL:
                    if(blend_index < 100) {
                        blend_index += 10;
                        std::cout << "acidcam: Blend Index Set to: " << blend_index << ".\n";
                    }
                    break;
                case GLFW_KEY_UP:
                    if(shader_index > 0) {
                        --shader_index;
                        setShader(shader_index);
                    }
                    break;
                case GLFW_KEY_DOWN:
                    if(shader_index < shaders.size()-1) {
                        shader_index++;
                        setShader(shader_index);
                    }
                    break;
                case GLFW_KEY_H:
                    std::cout << "acidcam: Playlist shuffled...\n";
                    sortPlaylist();
                    break;
                case GLFW_KEY_G:
                    
                    if(paused) {
                        std::cout << "acidcam: program unpaused\n";
                        paused = false;
                    } else {
                        std::cout << "acidcam: program paused...\n";
                        paused = true;
                    }
                    
                    break;
                case GLFW_KEY_Q:
                    optx[0] -= movement_rate;
                    break;
                case GLFW_KEY_W:
                    optx[0] += movement_rate;
                    break;
                case GLFW_KEY_E:
                    optx[1] -= movement_rate;
                    break;
                case GLFW_KEY_R:
                    optx[1] += movement_rate;
                    break;
                case GLFW_KEY_Y:
                    optx[2] -= movement_rate;
                    break;
                case GLFW_KEY_U:
                    optx[2] += movement_rate;
                    break;
                case GLFW_KEY_I:
                    optx[3] -= movement_rate;
                    break;
                case GLFW_KEY_O:
                    optx[3] += movement_rate;
                    break;
                case GLFW_KEY_T:
                    optx = glm::vec4(0.5, 0.5, 0.5, 0.5);
                    break;
                case GLFW_KEY_PAGE_UP: {
                    stored_position = index;
                    stored_var_position = var_index;
                    
                    std::string namex;
                    if(stored_position >= 0 && stored_position < ac::solo_filter.size()) {
                        namex = ac::solo_filter[stored_position];
                    } else {
                        namex = "Unknown";
                    }
                    
                    std::cout << "acidcam: Stored position set to: " << namex << " - " << stored_position << " playlist position: " << stored_var_position << "\n";
                }
                    break;
                case GLFW_KEY_PAGE_DOWN: {
                    if(list_enabled == false)
                        index = stored_position;
                    else
                        index = var_list[stored_var_position];
                    std::string namex;
                    if(index >= 0 && index < ac::solo_filter.size()) {
                        namex = ac::solo_filter[index];
                    } else {
                        namex = "Unknown";
                    }
                    std::cout << "acidcam: Index set to stored position: " << namex << " - " << index << "\n";
                }
                    break;
                case GLFW_KEY_SLASH:
                    rand_shader = !rand_shader;
                    std::cout << "acidcam: Random Shader Toggled...\n";
                    break;
                case GLFW_KEY_DELETE:
                    shader_list_enabled = !shader_list_enabled;
                    if(shader_list_enabled) {
                        std::cout << "acidcam: shader list enabled: " << shader_list.size() << " shaders.\n";
                    }
                    else
                        std::cout << "acidcam: shader list disabled.\n";
                        
                    break;
            }
        }
#ifdef SYPHON_SERVER
        if(redirect != 0) {
            sendString(redirect->getString());
        }
#endif
    }
    
    void resize(int newWidth, int newHeight) {
        aspect = (float)newWidth / (float)newHeight;
        glViewport(0, 0, newWidth, newHeight);
        p_mat = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f);
        window_width = newWidth;
        window_height = newHeight;
#ifdef SYPHON_SERVER
        if(syphon_enabled) {
            syphon_size(width, height);
        }
#endif
    }
    
    //std:::unordered_map<std::string, std::vector<std::string>> custom_filters;
    
    using List = std::vector<std::string>;
    
    std::unordered_map<std::string, List> custom_filters;
    
    void CallCustom(std::string index, cv::Mat &frame) {
        std::string val = index;
        if(custom_filters.find(val) != custom_filters.end()) {
            for(int i = 0; i < custom_filters[val].size(); ++i) {
                ac::CallFilter(custom_filters[val].at(i),frame);
            }
        } else {
            ac::CallFilter(index, frame);
        }
    }
    
    void loadCustom(std::string &c) {
        
        std::string path = c + "/index.txt";
        std::fstream file;
        file.open(path, std::ios::in);
        if(!file.is_open()) {
            std::cout << "acidcam: Couldn't load custom files: " << path << "\n";
            acidcam::updateError();
        }
        while(!file.eof()) {
            std::string n;
            std::getline(file, n);
            if(file) {
                std::fstream custom;
                custom.open(c + "/" + n, std::ios::in);
                if(!custom.is_open()) {
                    std::cout << "acidcam: Error loading custom file: " << n << "\n";
                    acidcam::updateError();
                }
                custom_filters[n] = std::vector<std::string>();
                while(!custom.eof()) {
                    std::string v;
                    std::getline(custom, v);
                    if(custom) {
                        custom_filters[n].push_back(v);
                    }
                }
                std::cout << "acidcam: Load Custom: " << n << "\n";
                ac::solo_filter.push_back(n);
                custom.close();
            }
        }
    }
    
    void loadShaderList(std::string lst) {
        std::fstream file;
        file.open(lst, std::ios::in);
        
        if(!file.is_open()) {
            std::cout << "acidcam: Error could not load shader list...\n";
            acidcam::updateError();
        }
        
        while(!file.eof()) {
            std::string s;
            std::getline(file, s);
            if(file)
                shader_list.push_back(shader_map[s]);
        }
        std::cout << "acidcam: shader list loaded...\n";
    }
    
    void loadShaders(const std::string &text) {
        std::ostringstream stream;
        stream << text << "/" << "index.txt";
        std::fstream file;
        file.open(stream.str(), std::ios::in) ;
        if(!file.is_open()) {
            std::cout << "acidcam: Error could not open: " << stream.str() << "\n";
            acidcam::updateError();
        }
        
        int index = 0;
        
        while(!file.eof()) {
            
            if(acidcam::redir == 1) {
#ifndef _WIN32
                if(redirect != 0) {
                    std::string text = redirect->getString();
                    sendString(text);
                }
#endif
            }
            
            std::string s;
            std::getline(file, s);
            if(file && s.length() > 0) {
                if(material_on == false && s.find("material") != std::string::npos)
                    continue;
                std::ostringstream fs1,fs2;
                fs1 << text << "/" << s;
                fs2 << text << "/vertex.glsl";
                ShaderProgram p;
                std::cout << "acidcam: Compiling Shader " << index << " [" << s << "] ";
                if(p.loadProgram(fs2.str(), fs1.str())==false) {
                    std::cout << "Error could not load: " << fs1.str() << "\n";
                    acidcam::updateError();
                }
                shader_map[s] = index;
                std::cout << "\n";
                p.setName(s.substr(0, s.rfind(".")));
                shaders.push_back(p);
                ++index;
            }
        }
        file.close();
        std::cout << "acidcam: Loaded " << shaders.size() << " Shaders...\n";
    }
};
}