summaryrefslogtreecommitdiff
path: root/sv.c
diff options
context:
space:
mode:
Diffstat (limited to 'sv.c')
-rw-r--r--sv.c283
1 files changed, 283 insertions, 0 deletions
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 <GL/glew.h>
+#include <GLFW/glfw3.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <alsa/asoundlib.h>
+
+#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);
+}