From 425dc14482011357522fee1713bdb88095bc9622 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 30 Nov 2010 06:01:21 +0100 Subject: [PATCH] Add MIDI viewer / visualization --- examples/CMakeLists.txt | 4 + examples/midiview.c | 283 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 287 insertions(+) create mode 100644 examples/midiview.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 60dd6c1..1056656 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -27,3 +27,7 @@ target_link_libraries(simple openlase) add_executable(pong pong.c) target_link_libraries(pong openlase) + +find_package(ALSA REQUIRED) +add_executable(midiview midiview.c) +target_link_libraries(midiview openlase ${ALSA_LIBRARIES}) diff --git a/examples/midiview.c b/examples/midiview.c new file mode 100644 index 0000000..c211bd0 --- /dev/null +++ b/examples/midiview.c @@ -0,0 +1,283 @@ +/* + OpenLase - a realtime laser graphics toolkit + +Copyright (C) 2009-2010 Hector Martin "marcan" + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 or version 3. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "libol.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +int portid; + +struct blip { + int active; + float ts; + float age; + float x, y; + float r; + float phase; + float bright; + int note; +}; + +float notespeed = 1.0; +float avgnotes = 0.0; + +#define NUM_BLIPS 32 + +int cur_blip = 0; +struct blip blips[NUM_BLIPS]; + +snd_seq_t *open_seq(void) { + snd_seq_t *seq_handle; + + if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, 0) < 0) { + fprintf(stderr, "Error opening ALSA sequencer.\n"); + return NULL; + } + snd_seq_set_client_name(seq_handle, "midiview"); + if ((portid = snd_seq_create_simple_port(seq_handle, "midiview", + SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_APPLICATION)) < 0) { + fprintf(stderr, "Error creating sequencer port.\n"); + return NULL; + } + return seq_handle; +} + +void note_on(int note, int velocity) +{ + float ts = avgnotes; + if (ts > 20) + ts = 20; + + blips[cur_blip].active = 1; + blips[cur_blip].ts = ts; + int xp = (note) % 12; + blips[cur_blip].x = (xp / 6.0 - 1.0)* 0.6; + blips[cur_blip].y = (2*note/127.0)-1.0; + blips[cur_blip].r = 0.05; + blips[cur_blip].bright = 0.3+(velocity / 70.0); + blips[cur_blip].phase = 1; + blips[cur_blip].note = 1; + + cur_blip = (cur_blip + 1) % NUM_BLIPS; +} + +int midi_action(snd_seq_t *seq_handle) { + + snd_seq_event_t *ev; + int cnt = 0; + + do { + snd_seq_event_input(seq_handle, &ev); + switch (ev->type) { + case SND_SEQ_EVENT_NOTEON: + if (ev->data.note.velocity != 0) { + note_on(ev->data.note.note, ev->data.note.velocity); + cnt++; + } + } + snd_seq_free_event(ev); + } while (snd_seq_event_input_pending(seq_handle, 0) > 0); + return cnt; +} + +#define CP 0.55228475f + +void circle(float x, float y, float r, uint32_t color) +{ + olPushMatrix(); + olPushColor(); + olMultColor(color); + olTranslate(x, y); + olRotate((x+y)*123); + /*olBegin(OL_BEZIERSTRIP); + olVertex(0, r, color); + int i; + for (i=0; i<2; i++) { + olVertex(CP*r, r, C_WHITE); + olVertex(r, CP*r, C_WHITE); + olVertex(r, 0, C_WHITE); + olVertex(r, -CP*r, C_WHITE); + olVertex(CP*r, -r, C_WHITE); + olVertex(0, -r, C_WHITE); + olVertex(-CP*r, -r, C_WHITE); + olVertex(-r, -CP*r, C_WHITE); + olVertex(-r, 0, C_WHITE); + olVertex(-r, CP*r, C_WHITE); + olVertex(-CP*r, r, C_WHITE); + olVertex(0, r, C_WHITE); + }*/ +/* olVertex(0, r, color); + olVertex(0, r, color); + olVertex(r*0.1, r, color); + olVertex(r*0.1, r, color);*/ + + float circum = 2 * M_PI * r; + int segments = circum / (1/30.0); + if (segments < 50) + segments = 50; + int i; + olBegin(OL_POINTS); + olVertex(r, 0, 0); + for (i=0; i<=(2*segments+10); i++) { + float w = i * M_PI * 2.0 / segments; + uint32_t c = C_WHITE; + if (i > 2*segments) + c = C_GREY((10-(i-segments)) * 28); + else if (i < 3) + c = C_GREY(i * 85); + olVertex(r*cosf(w), r*sinf(w), c); + } + olEnd(); + olPopColor(); + olPopMatrix(); +} + +int draw(void) +{ + int i; + int cnt = 0; + for (i=0; iactive) { + float br = b->bright * b->phase * 255.0; + br = br > 255 ? 255 : br; + uint32_t col = C_GREY((int)br); + circle(b->x, b->y, b->r, col); + cnt++; + } + } + return cnt; +} + +void animate(float time) +{ + int i; + float ts = avgnotes; + if (ts > 20) + ts = 20; + for (i=0; iactive) { + //if (b->age < 0.3 && b->ts < ts) + // b->ts = ts; + float bt = time * ts/14; + b->r *= powf(300.0f, bt); + //b->r += bt*1.4; + b->phase -= bt * 2; + if (b->r > 0.7 || b->phase < 0 || b->bright * b->phase < 0.3) + b->active = 0; + b->age += time; + } + } +} + +int main (int argc, char *argv[]) +{ + + snd_seq_t *seq_handle; + int npfd; + struct pollfd *pfd; + + seq_handle = open_seq(); + npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN); + pfd = malloc(npfd * sizeof(*pfd)); + snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN); + + if (argc == 2) { + snd_seq_addr_t addr; + if (snd_seq_parse_address(seq_handle, &addr, argv[1]) == 0) { + if (snd_seq_connect_from(seq_handle, portid, addr.client, addr.port) == 0) { + printf("Connected to %s\n", argv[1]); + } + } + } + + OLRenderParams params; + + memset(¶ms, 0, sizeof params); + params.rate = 48000; + params.on_speed = 2.0/50.0; + params.off_speed = 2.0/35.0; + params.start_wait = 6; + params.start_dwell = 1; + params.curve_dwell = 0; + params.corner_dwell = 0; + params.curve_angle = cosf(30.0*(M_PI/180.0)); // 30 deg + params.end_dwell = 0; + params.end_wait = 7; + params.flatness = 0.00001; + params.snap = 1/100000.0; + params.render_flags = RENDER_GRAYSCALE; + + if(olInit(3, 30000) < 0) + return 1; + + olSetRenderParams(¶ms); + + float time = 0; + float ftime; + int i,j; + + int frames = 0; + + float nps = 0; + avgnotes = 5; + + while(1) { + olLoadIdentity(); + + int drawn = draw(); + if (drawn < 2) + draw(); + + ftime = olRenderFrame(100); + + float t = powf(0.3, ftime); + avgnotes = avgnotes * t + (nps+2) * (1.0f-t); + + animate(ftime); + + int notes = 0; + if (poll(pfd, npfd, 0) > 0) + notes = midi_action(seq_handle); + + int pnotes = notes; + if (pnotes > 1) + pnotes = 1; + + nps = pnotes / ftime * 1.2; + + frames++; + time += ftime; + printf("Frame time: %f, Avg FPS:%f, Cur FPS:%f, %d, nps:%3d, avgnotes:%f\n", ftime, frames/time, 1/ftime, notes, (int)nps, avgnotes); + } + + olShutdown(); + exit (0); +} +