MIDI Animation, need help with some advanced MIDI mechanics
in
Contributed Library Questions
•
2 years ago
Hello there forum, first post though I've crawled a bit looking for info.
I've been working on creating MIDI based animations and have started hitting some road blocks as Processing is my first coding platform (after I toyed with Java to get some basics down).
Here's a sample of what I'm making:
http://vimeo.com/30363314 . I aim to make more complex things for my own music, as I have more control over that, but need help!
Initial learning attempt (with some of my music):
http://vimeo.com/30696060 .
While I'm still way off from figuring out how to render and sync audio properly, there's a lot I need to figure out how to do first. Here is the basic code I've been using, I'll specify what I need - and what I've found as issues - afterward. This is of a somewhat more advanced version.
- import promidi.*;
- import processing.opengl.PGraphicsOpenGL;
- MidiIO midiIO;
- static Pill[] pills;
- static int pillTop;
- int h = 600;
- int w = 1100;
- int x = w /2;
- int y = h /2;
- int arraySize = 1024;
- /* ===== setup ===== */
- void setup() {
- size(w, h);
- smooth();
- frameRate(60);
- midiIO = MidiIO.getInstance(this); // grabs midi devices
- midiIO.printDevices(); // display inputs for debugging
- for(int i = 0; i <=15; i++) {
- midiIO.openInput(0, i); // open all inputs
- }
- pills = new Pill[arraySize]; // sets up array for pills
- pillTop = 0;
- }
- /* ===== draw ===== */
- void draw() {
- background(10);
- for (int i = 0; i < pillTop; i++) {
- pills[i].render();
- pills[i].update();
- }
- }
- /* ===== methods ===== */
- void noteOn(Note note, int deviceNumber, int midiChan) {
- noteDisp(note, midiChan);
- }
- void noteDisp(Note note, int midiChan) {
- if (pillTop <= arraySize) {
- pills[pillTop] = new Pill(x, y, note, midiChan);
- pills[pillTop].tag = pillTop;
- pillTop++;
- }
- }
- void noteOff(Note note, int deviceNumber, int midiChan) {
- for (int i = 0; i < pillTop; i++) {
- if (pills[i].note == note) {
- killPill(pills[i], note);
- break;
- }
- }
- }
- static void killPill(Pill p, Note note) {
- pillTop--;
- pills[p.tag] = pills[pillTop];
- pills[p.tag].tag = p.tag;
- }
- class Pill {
- int tag;
- int x, y;
- Note note;
- int midiChan;
- int h, s, b, a, ab;
- float speedX, speedY;
- float rotator = 360;
- float rotAdd = 8;
- float pitMult = 1;
- int rotCut = 180;
- int rotChange = 4;
- int aCut = 10;
- int aChange = 3;
- int abChange = 50;
- Pill(int nx, int ny, Note n, int mc) {
- x = nx;
- y = ny;
- speedX = 0;
- speedY = 0;
- midiChan = mc;
- colorMode(HSB, 100); // hue, sat, brightness
- h = midiChan *10;
- s = 100;
- b = n.getVelocity();
- a = 255;
- ab = 255;
- note = n;
- int pit = note.getPitch();
- int len = note.getLength();
- int vel = note.getVelocity();
- }
- void render() {
- float mappedPitch = map(note.getPitch(), 70, 90, 100, 200);
- ellipseMode(CENTER);
- rectMode(CENTER);
- strokeWeight(1);
- noFill();
- strokeWeight(1);
- stroke(h, s, b, ab);
- /* current noteOn mult */
- for(int i = x; i <= x +20; i += 5) {
- arc(
- x, y,
- i +10 +mappedPitch *pitMult, i +mappedPitch *pitMult,
- radians(360), radians(360 +20)
- );
- }
- /* current noteOn displayer */
- strokeWeight(4);
- arc(
- x, y,
- x +mappedPitch *pitMult, x +mappedPitch *pitMult,
- radians(360), radians(360 +20)
- );
- /* radial note pushing */
- stroke(h, s, b, a);
- if(midiChan == 0) {
- strokeWeight(4);
- arc(
- x, y,
- x +mappedPitch *pitMult, x +mappedPitch *pitMult,
- radians(rotator), radians(rotator +rotAdd)
- );
- }
- else {
- strokeWeight(2);
- arc(
- x, y,
- x +mappedPitch *pitMult, x +mappedPitch *pitMult,
- radians(rotator), radians(rotator +rotAdd)
- );
- }
- }
- void update() {
- if(rotator >= rotCut) {
- rotator -= rotChange;
- speedY = 0;
- speedX = 0;
- x += speedX;
- y += speedY;
- ab -= abChange;
- }
- else {
- rotator -= rotChange;
- if(pitMult >= -0.7)
- pitMult -= 0.06;
- else
- pitMult = 0;
- a -= aChange; // lower opacity for removal
- }
- if (a <= aCut)
- miditest05i.killPill(this, note); // remove pill
- }
- }
This is all done with ProMidi. Trying to reach for some things has led me to RWMidi (though I can't use it because I can't figure out how to separate the channels like I have here).
These are the things that I most want to be able to mess with, though some of them may be limitations of ProMidi. It seems I need to explain my setup first.
Setup: Logic Pro outputs audio on soft synth midi channels, using cloned external midi outs to send midi data, with different channels for each instrument. Track is played and screen is captured. Thus is a real time render - midi data is displayed as it happens and is captured, though I would greatly like this not to be for complexity reasons. I do NOT need to be able to do this live - this project is more to simply create visuals for music that I can upload to have something to watch with the music. This has led to a lot of dead ends while searching the forums, as most need real time abilities.
- Producing arbitrary "groups" of notes: Example, groups of notes based on the time spawn of a single measure. While I certainly can't see how to do this with ProMidi, I understand that RWMidi has binary sysex reference that might be able to check time codes, which would help with some other problems.
- Note length: ProMidi can't handle this because it's setup to defined lengths. I've tried several while() and if() loops in order to try to tie display length to terminate when noteOff (or Velocity = 0) is sent. Nothing has worked, though I wonder more if it's simply that I'm not calling a note from the pills[] array to specify which to remove, of which I'm not sure how to. If you remove the noteOff code, you can see visual references to the noteOff markers, which seems like I should easily be able to do this, but I can't figure out how to.
- Note relations: To do more complex rendering, I'd need to be able to keep track of the X and Y of previous notes, so that, for example, I can draw a line from a previously rendered pill to a new "current" pill. I've tried creating variables that hold info but I'm fairly certain my placement is what is messing up - mostly static call issues.
- Storing MIDI data: A large inspiration for this project was MAM, or the Music Animation Machine. The creator managed to read MIDI data and I assume store it in a way that "future" notes (notes that will "play" after the current note) could be displayed. I figure that this could be done if I store the MIDI data first, and then render it non-realtime, but I'm at a loss as how to how to do this without, again, midi timing.
- Render maximums: I cannot render enough lines to complete my design goals. Usually it tends to slow gradually, eventually to freezing, if I do not remove the pills quickly enough. While this can be solved by, I assume, simply rendering separate tracks to video at a time and then combining them, or rendering frames to disk in non-realtime, I cannot do either at the moment. I do not understand how to eventually render these properly in full scope.
I've gotten to this point so far with some help from a java coder friend who cleaned my code up and placed this pill array into it. Previously I was using several arrays to capture and render data which was pretty slow.
Mostly I tinker with it to get the aesthetics I want, changing render and associated placements.
If anyone can please give me some insight, examples, or ideas of what to look into, I'll be glad to study whatever I need to get this going. But at this point I'm hitting walls and simply creating more smaller projects instead of fixing what's needed in the design.
So, any help would be greatly appreciated!
While I am hesitant to put this code up, I realize it could help some others who have faced the same issues, so I'm willing to try to get some insight outside of simply asking my coder friend so that others may get a better idea of how to set up some MIDI-enabled visuals in a non-realtime environment.
1