#include #include #include #include #include #include #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #define MIDICHK(stmt, msg) if((stmt) < 0) {puts("ERROR: "#msg); exit(1);} int width = 1920; int height = 1080; GLFWwindow* window; GLuint vertex; GLuint fragment; GLuint shaderProgram; GLuint backbuffer; typedef struct Shad { GLuint id; char fragment[40]; time_t mtime; int new; } Shader; Shader shader; typedef struct Img { GLuint id; char path[40]; int new; } Image; Image images[8]; float params[4]; int record = 0; int recording = 0; int fullscreen = 0; FILE* ffmpeg; static snd_seq_t *midi_seq; static int midiin; int ticks =0; int running = 0; int program=0; int frame = 0; unsigned char buffer[1920*1080*4]; struct arg_struct { unsigned char buffer[1920*1080*4]; int frame; }; void *screenshot(void * f) { unsigned char pixels[width*height*4]; //time_t current_time = time(NULL); //char output_file[25]; char output_file[20]; //strftime(output_file, 25, "%Y-%m-%d_%H%M%S.png", localtime(¤t_time)); sprintf(output_file, "record/%i.png",*(int*)f); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Flip the image on Y int depth = 4; int row,col,z; stbi_uc temp; for (row = 0; row < (height>>1); row++) { for (col = 0; col < width; col++) { for (z = 0; z < depth; z++) { temp = pixels[(row * width + col) * depth + z]; pixels[(row * width + col) * depth + z] = pixels[((height - row - 1) * width + col) * depth + z]; pixels[((height - row - 1) * width + col) * depth + z] = temp; } } } printf("%s\n",output_file); if (0 == stbi_write_png(output_file, width, height, 4, pixels, width * 4)) { printf("can't create file %s",output_file); } } void *save(void * b) { //struct arg_struct args; //glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); char output_file[20]; sprintf(output_file, "record/%i.png",frame); printf("%ix%i\n",width,height); printf("%s\n",output_file); int r = stbi_write_png(output_file, width, height, 4, buffer, width * 4); printf("%s\n",output_file); printf("%i\n",r); //struct arg_struct *args = arguments; //char output_file[20]; //sprintf(output_file, "record/%i.png",frame); //printf("%s\n",output_file); //int r = stbi_write_png(output_file, width, height, 4, buffer, width * 4); //printf("%i\n",r); //if (0 == stbi_write_png(output_file, width, height, 4, buffer, width * 4)) { printf("can't create file %s",output_file); } return(NULL); } static void error_callback(int error, const char* description) { fputs(description, stderr); } static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (key == GLFW_KEY_Q && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE); //else if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) screenshot(); } const char * readShader(char * path) { FILE *file = fopen(path, "r"); if (!file) { fprintf(stderr,"Cannot read %s\n",path); } char source[100000]; int res = fread(source,1,100000-1,file); source[res] = 0; fclose(file); const char *c_str = source; return c_str; } GLuint compileShader(const char * source, GLenum type) { GLuint sh = glCreateShader(type); glShaderSource(sh, 1, &source, NULL); glCompileShader(sh); int success; glGetShaderiv(sh, GL_COMPILE_STATUS, &success); if (!success) { char infoLog[512]; glGetShaderInfoLog(sh, 512, NULL, infoLog); fprintf(stderr,"Shader compilation failed: %s\n",infoLog); } else { return sh; } } GLuint linkShader(GLuint vertex, GLuint fragment) { glUseProgram(0); glDeleteProgram(shader.id); shader.id = glCreateProgram(); glAttachShader(shader.id, vertex); glAttachShader(shader.id, fragment); glLinkProgram(shader.id); int success; glGetProgramiv(shader.id, GL_LINK_STATUS, &success); if (!success) { char infoLog[512]; glGetProgramInfoLog(shader.id, 512, NULL, infoLog); fprintf(stderr,"Shader linking failed: %s\n",infoLog); } glDetachShader(shader.id, vertex); glDetachShader(shader.id, fragment); glDeleteShader(vertex); glDeleteShader(fragment); } void createShader() { static const char vertex_src[] = { "#version 450 core\n" "const vec2 quadVertices[4] = { vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0) };\n" "void main() { gl_Position = vec4(quadVertices[gl_VertexID], 0.0, 1.0); }\n" }; vertex = compileShader(vertex_src, GL_VERTEX_SHADER); fragment = compileShader(readShader(shader.fragment),GL_FRAGMENT_SHADER); linkShader(vertex,fragment); struct stat file_stat; stat(shader.fragment, &file_stat); shader.mtime = file_stat.st_mtime; glUseProgram(shader.id); }; void imageUniforms(int i) { glUniform1i(glGetUniformLocation(shader.id, "images")+i,i); } void resize_callback(GLFWwindow* window, int w, int h) { width = w; height = h; glUniform2f(glGetUniformLocation(shader.id, "resolution"),width,height); } void readImage(int i) { images[i].new = 1; int w,h,comp; stbi_set_flip_vertically_on_load(1); unsigned char* pixels = stbi_load(images[i].path, &w, &h, &comp, STBI_rgb_alpha); imageUniforms(i); glActiveTexture(GL_TEXTURE0+i); glGenTextures(1, &images[i].id); glBindTexture(GL_TEXTURE_2D,images[i].id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); stbi_image_free(pixels); } void *readMidi() { MIDICHK(snd_seq_open(&midi_seq, "default", SND_SEQ_OPEN_INPUT, 0), "Could not open sequencer"); MIDICHK(snd_seq_set_client_name(midi_seq, "SV"), "Could not set client name"); MIDICHK(midiin = snd_seq_create_simple_port(midi_seq, "sv:in", SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION), "Could not open port"); while(1) { snd_seq_event_t *ev = NULL; snd_seq_event_input(midi_seq, &ev); if (ev->type == SND_SEQ_EVENT_NOTEON) { int note = ev->data.note.note; if (note >= 36) program = note-36; } else if(ev->type == SND_SEQ_EVENT_CONTROLLER) { int p = ev->data.control.param; int v = ev->data.control.value; if (p < 4) params[p] = (float)v/127.0; } else if(ev->type == SND_SEQ_EVENT_SONGPOS) ticks = ev->data.control.value*24; else if(ev->type == SND_SEQ_EVENT_START) { running = 1; if (record) { /* time_t current_time = time(NULL); char output_file[25]; strftime(output_file, 25, "%Y-%m-%d_%H%M%S.mkv", localtime(¤t_time)); char cmd[250]; sprintf(cmd, "ffmpeg -framerate 60 -f rawvideo -pix_fmt rgba -s %ix%i -i - -preset ultrafast -y -c:v libx264 -crf 0 -vf vflip %s",width,height,output_file); ffmpeg = popen(cmd, "w"); */ frame = 0; recording = 1; } } else if(ev->type == SND_SEQ_EVENT_STOP) { running = 0; if (recording) { recording = 0; //pclose(ffmpeg); } } else if(ev->type == SND_SEQ_EVENT_CONTINUE) running = 1; else if(ev->type == SND_SEQ_EVENT_CLOCK) if (running) ticks++; } } void *watchShader() { while (1) { sleep(0.2); struct stat file_stat; stat(shader.fragment, &file_stat); if (file_stat.st_mtime > shader.mtime) shader.new = 1; } } int main(int argc, char **argv) { // parse options int opt; while ((opt = getopt(argc, argv, "rf")) != -1) { switch (opt) { case 'r': record = 1; fullscreen = 1; break; case 'f': fullscreen = 1; break; } } for (int i = optind; iwidth; height = mode->height; if (fullscreen) { window = glfwCreateWindow(width,height, "", glfwGetPrimaryMonitor(), NULL); } else { window = glfwCreateWindow(width,height, "sv", NULL, NULL); glfwSetWindowSizeCallback(window, resize_callback); } glfwMakeContextCurrent(window); glfwSetKeyCallback(window, key_callback); glfwSetErrorCallback(error_callback); glewInit(); strncpy(shader.fragment,"shader.frag",40); shader.new = 1; unsigned int VAO; glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); glfwGetWindowSize(window, &width, &height); glCreateTextures(GL_TEXTURE_2D,1,&backbuffer); glTextureStorage2D(backbuffer,1,GL_RGBA32F,width,height); glBindImageTexture(0,backbuffer, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); for (int i = 0; i<8; i++) { readImage(i); images[i].new = 0; } pthread_t midiin_t; pthread_create(&midiin_t, NULL, readMidi, NULL); pthread_t frag_t; pthread_create(&frag_t, NULL, watchShader, NULL); int n = 0; printf("%ix%i\n",width,height); while (!glfwWindowShouldClose(window)) { if (shader.new) { createShader(); glUniform2f(glGetUniformLocation(shader.id, "resolution"),width,height); // important!! for (int i = 0; i<8; i++) { imageUniforms(i); } shader.new = 0; } glUniform1f(glGetUniformLocation(shader.id, "time"),glfwGetTime()); glUniform1f(glGetUniformLocation(shader.id, "ticks"),(float)ticks); glUniform1i(glGetUniformLocation(shader.id, "program"),program); for (int i = 0; i<4; i++) { glUniform1f(glGetUniformLocation(shader.id, "params")+i, params[i]); } glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glfwSwapBuffers(window); if (recording) { /* pid_t pid = fork(); if (pid == 0) { unsigned char buffer[width*height*4]; //struct arg_struct args; glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); char output_file[20]; sprintf(output_file, "record/%i.png",frame); printf("%s\n",output_file); int r = stbi_write_png(output_file, width, height, 4, buffer, width * 4); printf("%i\n",r); exit(EXIT_SUCCESS); } */ //args.buffer = buffer; //args.frame = frame; glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); pthread_t tid; pthread_create (&tid, NULL, save, NULL); pthread_detach (tid); //printf("%i\n",frame); frame++; //fwrite(buffer, sizeof(int)*width*height, 1, ffmpeg); } glfwPollEvents(); } //if (recording) pclose(ffmpeg); pthread_cancel(midiin_t); pthread_cancel(frag_t); glfwDestroyWindow(window); glfwTerminate(); exit(EXIT_SUCCESS); }