From 2bc8c17a1b5c0ce6f45b60ad208fd358729f1b73 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Mon, 29 Apr 2019 16:15:21 +0200 Subject: initial commit --- sv.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 sv.c (limited to 'sv.c') diff --git a/sv.c b/sv.c new file mode 100644 index 0000000..cc5f851 --- /dev/null +++ b/sv.c @@ -0,0 +1,283 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +int width; +int height; +GLFWwindow* window; + +GLuint shader_id; +time_t shader_mtime; + +int nr_images = 0; +time_t images_mtime; + +GLuint backbuffer; +GLfloat params[8]; +int scene; + +double ticks = 0; +float last_tick; +float tick_dur; +int running = 0; + +FILE* ffmpeg; +static snd_seq_t *midi_seq; +static int midiin; + +float now() { + struct timespec tv; + clock_gettime(CLOCK_MONOTONIC, &tv); + return (float) (tv.tv_sec * 1000000000 + tv.tv_nsec)/1000000000; +} + +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 (action == GLFW_PRESS) { + if (key == GLFW_KEY_Q) + glfwSetWindowShouldClose(window, GL_TRUE); + else if (key == GLFW_KEY_SPACE) { + if (running) { + system("oscsend 0.0.0.0 8888 /renoise/transport/stop"); + running = 0; + } + else { + system("oscsend 0.0.0.0 8888 /renoise/transport/start"); + running = 1; + } + } + } +} + +void resize_callback(GLFWwindow* window, int w, int h) { + width = w; + height = h; + glUniform2f(glGetUniformLocation(shader_id, "resolution"),width,height); +} + +time_t mtime(char *path) { + struct stat file_stat; + stat(path, &file_stat); + return(file_stat.st_mtime); +} + +const char * readShader() { + char source[100000]; + FILE *file = fopen("shader.frag", "r"); + if (!file) { + fprintf(stderr,"Cannot read 'shader.frag'\n"); + exit(EXIT_FAILURE); + } + int res = fread(source,1,100000-1,file); + source[res] = 0; + fclose(file); + const char *c_str = source; + return c_str; +} + +void checkShader(GLuint id,int success, char * action) { + if (!success) { + char infoLog[512]; + glGetShaderInfoLog(id, 512, NULL, infoLog); + fprintf(stderr,"Shader %s failed: %s\n",action,infoLog); + exit(EXIT_FAILURE); + } +} + +GLuint compileShader(const char * source, GLenum type) { + GLuint id = glCreateShader(type); + glShaderSource(id, 1, &source, NULL); + glCompileShader(id); + int success; + glGetShaderiv(id, GL_COMPILE_STATUS, &success); + checkShader(id,success,"compilation"); + return id; +} + +GLuint linkShader(GLuint vertex, GLuint fragment) { + GLuint id = glCreateProgram(); + glAttachShader(id, vertex); + glAttachShader(id, fragment); + glLinkProgram(id); + int success; + glGetProgramiv(id, GL_LINK_STATUS, &success); + checkShader(id,success,"linking"); + glDetachShader(id, vertex); + glDetachShader(id, fragment); + glDeleteShader(vertex); + glDeleteShader(fragment); + return id; +} + +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" + }; + GLuint vertex = compileShader(vertex_src, GL_VERTEX_SHADER); + GLuint fragment = compileShader(readShader(),GL_FRAGMENT_SHADER); + shader_id = linkShader(vertex,fragment); + shader_mtime = mtime("shader.frag"); + glUseProgram(shader_id); + glUniform2f(glGetUniformLocation(shader_id, "resolution"),width,height); // important!! +} + +void readImage(char * path, int n) { + + int w,h,comp; + stbi_set_flip_vertically_on_load(1); + unsigned char* pixels = stbi_load(path, &w, &h, &comp, STBI_rgb_alpha); + glUniform1i(glGetUniformLocation(shader_id, "images")+n,n); + + GLuint id; + glActiveTexture(GL_TEXTURE0+n); + glGenTextures(1, &id); + fprintf(stderr,"Loading image #%i %s \n",n,path); + glBindTexture(GL_TEXTURE_2D,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); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + stbi_image_free(pixels); + +} + +void readImages() { + + FILE *file = fopen("images", "r"); + char * line = NULL; + size_t len = 0; + ssize_t chars; + int n = 0; + + while (((chars = getline(&line, &len, file)) != -1)) { + if (line[chars - 1] == '\n') { + line[chars - 1] = '\0'; + --chars; + } + readImage(line,n); + n++; + } + + nr_images = n; + fclose(file); + images_mtime = mtime("images"); +} + +void *readMidi() { + + snd_seq_open(&midi_seq, "default", SND_SEQ_OPEN_INPUT, 0); + snd_seq_set_client_name(midi_seq, "SV"); + 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); + + while(1) { + snd_seq_event_t *ev = NULL; + snd_seq_event_input(midi_seq, &ev); + if(running && ev->type == SND_SEQ_EVENT_CLOCK) { + float current_time = now(); + tick_dur = current_time-last_tick; + last_tick = current_time; + ticks++; + } + else if(ev->type == SND_SEQ_EVENT_NOTEON) + scene = ev->data.note.note; + else if(ev->type == SND_SEQ_EVENT_CONTROLLER) { + int p = ev->data.control.param; + int v = ev->data.control.value; + if (p < 8) 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; + else if(ev->type == SND_SEQ_EVENT_STOP) + running = 0; + else if(ev->type == SND_SEQ_EVENT_CONTINUE) + running = 1; + } +} + +int main(int argc, char **argv) { + + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_DECORATED, GL_FALSE); + const GLFWvidmode * mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + width = mode->width; + height = mode->height; + window = glfwCreateWindow(width,height, "sv", NULL, NULL); + glfwSetWindowSizeCallback(window, resize_callback); + glfwMakeContextCurrent(window); + glfwSetKeyCallback(window, key_callback); + glfwSetErrorCallback(error_callback); + glewInit(); + 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); + + createShader(); + readImages(); + + unsigned int VAO; + glGenVertexArrays(1, &VAO); + glBindVertexArray(VAO); + + pthread_t midiin_t; + pthread_create(&midiin_t, NULL, readMidi, NULL); + + GLuint ticks_id = glGetUniformLocation(shader_id, "ticks"); + GLuint scene_id = glGetUniformLocation(shader_id, "scene"); + GLuint params_id = glGetUniformLocation(shader_id, "params"); + GLuint backbuffer_id = glGetUniformLocation(shader_id, "backbuffer"); + + float next_frame = now(); + + while (!glfwWindowShouldClose(window)) { // glUniforms cannot be set from threads! + + if (mtime("shader.frag") > shader_mtime) { + createShader(); + for (int i = 0; i < nr_images; i++) glUniform1i(glGetUniformLocation(shader_id, "images")+i,i); + ticks_id = glGetUniformLocation(shader_id, "ticks"); + scene_id = glGetUniformLocation(shader_id, "scene"); + params_id = glGetUniformLocation(shader_id, "params"); + backbuffer_id = glGetUniformLocation(shader_id, "backbuffer"); + } + + if (mtime("images") > images_mtime) readImages(); + + //float tick_frac = ticks + (now()-last_tick)/tick_dur; + //glUniform1f(glGetUniformLocation(shader_id, "ticks"),tick_frac); + glUniform1i(ticks_id,ticks); + glUniform1i(scene_id, scene); + glUniform1fv(params_id, 8, params); + glUniform1i(backbuffer_id,0); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glfwSwapBuffers(window); + glfwPollEvents(); + } + + system("oscsend 0.0.0.0 8888 /renoise/transport/stop"); + pthread_cancel(midiin_t); + + glfwDestroyWindow(window); + glfwTerminate(); + + exit(EXIT_SUCCESS); +} -- cgit v1.2.3