Proyecto Piano3DLT ~~~~~~~~~~~~~~~~~~ Prototype "modelosteclas" Draw a piano keyboard and hand in pyramidal perspective, with rotation and panning control, and "move" a midi file End user has full control over: - two axis rotation, though mouse dragging - camera opening angle, through mouse wheel Version 0.3.6 Changelog from 0.3.5: - do some code and comments minor cleanup - really code angle calculation from MIDI events, but not using piece-wise-linear, but making instantaneous key pressing and releasing Changelog from 0.3.4: - Calculate each frame's key depression angle from MIDI events Originally wishing to calculate those angles as piece-wise linear function, with a minimum of 0, "maximun" -PI/64, starting to depress a fixed number of MIDI ticks before NOTE_ON event, and starting to lift the same fixed number of ticks before NOTE_OFF event. Now, that objective is marked PENDING (see 0.3.5 changelog). Initially, nor calculating those ticks before as a function of velocity or aftertouch, neither calculating possible interaction between succesive and time-next pressing of the same key. Already thinkinh about future interpolating "time-zoom" when very slow motion movement is required. Changelog from 0.3.3: - fix key depression angle index just before rotation - fix initial A key shape - add cool effect in key depression angle - load MIDI file bwv577.mid (located in sketch directory ) NOTE_ON/OFF messages - set static pieceTotalTime and midiTotalTicks for that piece, not from midi header Changelog from 0.3.2: - pressedKeyAngle: array of key depression angle, starting in A0 -> C8 - keyType: black or white keys location and order - remove physical unused keyboard parameters until needed again - arranging and grouping of parameters and variables - enlarge reference axis - keyLocation: octave key position data; still no actual algorithm change Changelog from 0.3.1: - define frames, total time and update time slider - key rotation: remove D key rotation and set frame based key rotation Changelog from 0.3.0: - add time slider in serparate window (from controlp5 library examples) Changelog from 0.2.3: - add primitive left hand model - remove toolbar and time slide Changelog from 0.2.2: - add bottom toolbar and experimental time slide - experimental "pulsation" (rotation) of all D keys Changelog from 0.2.1: - position change with left button, angle change with right - experimental "pulsation" (rotation) of all D keys Changelog from 0.2.0: - draw 5 octaves and adjust initial Changelog from 0.1.1: - code and comments translated into english / /********************* import libraries * / /* control window */ import controlP5.*; /* MIDI file reading */ import javax.sound.midi.Sequencer; import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiEvent; import javax.sound.midi.MidiMessage; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.InvalidMidiDataException; import java.io.IOException; import javax.sound.midi.Sequence; import javax.sound.midi.Track; import javax.sound.midi.ShortMessage; /************************************************************** point of view and near parameters / /* field of view */ float fov = PI/2; /* X angle of scene rotation */ float angleX=0; float previousAngleX=angleX; /* Y angle of scene rotation */ float angleY=PI/3+PI/2; float previousAngleY=angleY; /* X scene pan */ float positionX=0; float previousPositionX=positionX; /* Y scene pan */ float positionY=0; float previousPositionY=positionY; /* auxiliary variables for angle rotation when dragging */ boolean locked=false; int baseY; int difY; int baseX; int difX; /*********************************** Hand parameters and variables * / /* hand model parameters, finger numbering starts with thumb=0 */ /* horizontal angles */ /* thumb first */ /* initialise fingers slightly (bout 11ยบ) flexed */ float[] rightHandFingerHorizAngle = { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16 }; float[] leftHandFingerHorizAngle = { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16 }; /* vertical angles, "upper" angles first */ float[][] rightHandFingerVertAngle = { { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16}, { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16}, { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16}, { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16}, { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16} }; float[][] leftHandFingerVertAngle = { { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16}, { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16}, { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16}, { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16}, { -PI/16, -PI/16, -PI/16, -PI/16, -PI/16} }; /* hand dimensions, thumb=0, segment most near to palm=0 */ /* initially assume symmetric hands */ float[][] fingerSegmentWidth = { {0,0,0}, /* initially ignore thumb */ {20,19,18}, {20,19,18}, {19,18,17}, {16,14,15}, }; float[][] fingerSegmentHeight = { {0,0,0}, /* initially ignore thumb */ {20,17,15}, {19,19,15}, {18,18,15}, {16,15,14}, }; float[][] fingerSegmentLength = { {0,0,0}, /* initially ignore thumb */ {50,30,20}, {55,40,25}, {55,35,23}, {50,25,20}, }; /*************************************** Keyboard parameters and variables * / /* reference location for keys starting in C# respect to C and endding in next octave C respect to this octave's last B / float[] keyLocation = { 12.0, /* C# */ 10.0, /* D */ 19.0, /* D# */ 3.0, /* E */ 22.0, /* F */ 10.5, /* F# */ 11.5, /* G */ 15.5, /* G# */ 6.5, /* A */ 20.5, /* A# */ 1.5, /* B */ 22.0 /* C */ }; String[] keyType = { "w", /* C */ "b", /* C# */ "w", /* D */ "b", /* D# */ "w", /* E */ "w", /* F */ "b", /* F# */ "w", /* G */ "b", /* G# */ "w", /* A */ "b", /* A# */ "w", /* B */ }; /* first key in standard 88 key keyboard */ int initialKey = 9; /****************************************** time/frames parameters and variables * / /* time and frames, in seconds */ float pieceTotalTime = 172; /* for bwv577 while we learn how to read midi header */ int theFrameRate=30; int pieceTotalFrames = (int) (pieceTotalTime*theFrameRate); int nowFrame=0; int midiTotalTicks=123444 + 1; /* for bwv577 while we learn how to read midi header */ /****************************** key rotation in each frame * to be calculated from midi * file / ArrayList allFramesKeyAngles = new ArrayList(); /* initially, a fixed value, not derived from velocity */ /* time taken for the key to be pressed from angle 0 to max angle */ int attackTimeTicks=30; /* ticks taken for the key to be restored to rest position */ int releaseTimeTicks=30; /********************************************** control windows parameters and variables * / ControlP5 controlP5; ControlWindow controlWindow; Controller mySlider; public int sliderValue = 40; /*********************** MIDI file variables * / ArrayList pieceNotes; /************************************************************** environment setup / void setup() { size(800, 600, P3D); frameRate(theFrameRate); /* control elements in separate window */ controlP5 = new ControlP5(this); controlP5.setAutoDraw(false); controlWindow = controlP5.addControlWindow("controlP5window",100,100,400,200); controlWindow.hideCoordinates(); controlWindow.setUpdateMode(ControlWindow.ECONOMIC); controlWindow.frameRate(1); controlWindow.setDrawBackground(false); controlWindow.setBackground(color(40)); mySlider = controlP5.addSlider("sliderValue",0,pieceTotalFrames,40,40,100,10); mySlider.setWindow(controlWindow); /* define function for mouse wheel */ addMouseWheelListener(new java.awt.event.MouseWheelListener() { public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { mouseWheel(evt.getWheelRotation()); }}); /* load MIDI file, initially at 30 BPM, because we don't know the actual rate! */ pieceNotes = loadMidi("bwv577.mid", 30.0); /* reserve space for rotation vectors for all frames */ int reservedFrames = reserveRotationVectors(); println("Reserved frames: "+ reservedFrames); /* loop through chronologic note events and set key rotation angles */ int assignedNotes = assignRotationVectors(); println("Assigned Notes: "+ assignedNotes); } /* reserve space for key rotation vectors for all frames */ int reserveRotationVectors() { int f; for (f=0; f= pieceTotalFrames) { nowFrame=0; } lights(); background(64); float cameraY = height/2.0; float cameraZ = cameraY / tan(fov / 2.0); float aspect = float(width)/float(height); perspective(fov, aspect, cameraZ/10.0, cameraZ*10.0); // translate(width/8, height/2, 0); translate(positionX, positionY, 0); rotateX(-angleY); rotateY(-angleX); /* draw coordinate axis for initial reference */ stroke(#FF0000); line(0,0,0,500,0,0); stroke(#00FF00); line(0,0,0,0,500,0); stroke(#0000FF); line(0,0,0,0,0,500); stroke(#000000); /* draw standard 88 keys keyboard, starting with A0 */ for (int k=0, theKey=initialKey ; k<88; k++, theKey++) { /* reset theKey to C if reached next octave */ if (theKey == 12) theKey=0; /* 1/4 first of all, rotate key angle */ // float pressedKeyRotation = keyRotationAngle[i]; float pressedKeyRotation = ((KeyRotation) ( allFramesKeyAngles.get(nowFrame) )).m_keyAngle[k]; rotateZ(pressedKeyRotation); /* 2/4 draw generic black key or choose white key type */ if (keyType[theKey] == "b") { /* set color almost-black */ fill(#202020); drawBlackKey(); } else { /* set color almost-white */ noStroke(); fill(#F0F0F0); switch (theKey) { /* draw white keys type as required */ case 0: /* take into account possible final C */ if (k==88-1) { drawFinalC(); } else { drawCF(10); } break; case 2: drawDGA(3,3); break; case 4: drawBE(10); break; case 5: drawCF(12); break; case 7: drawDGA(2,6.5); break; case 9: /* take into account possible initial A */ if (k==0) { drawDGA(0,2); } else { drawDGA(6.5,2); } break; case 11: drawBE(9); break; } } /* 3/4 reset key rotation preparing for next key */ rotateZ(-pressedKeyRotation); /* 4/4 shift to the right to draw next key */ translate(0,0,keyLocation[theKey]); //println(keyLocation[theKey]); } /* experimental right hand drawn up in the air */ rotateY(PI); translate(-200,100,0); /* draw hand: right=0, left=1 */ drawHand(0); /* dibujar la ventana de control */ controlP5.draw(); } /* draw a hand */ void drawHand(int handSide) { /* right=0, left=1 */ fill(#996633); /* draw fingers; initially, skip thumb, so start with i=1, not i=0 and recursively draw segments of each finger */ for (int i=1; i<5; i++) { rotateY( leftHandFingerHorizAngle[i]); rotateZ( leftHandFingerVertAngle[i][0]); /* initially, 3 segments left to draw, except thumb, of course */ drawFinger(i, handSide, 0); rotateZ( -leftHandFingerVertAngle[i][0]); rotateY(-leftHandFingerHorizAngle[i]); translate(0,0,25); } } /* draw a finger recursively (really draws a finger segment and calls ifself) */ void drawFinger(int finger, int handSide, int segmentToDraw) { rotateZ( leftHandFingerVertAngle[finger][segmentToDraw]); drawCylinder( fingerSegmentLength[finger][segmentToDraw], fingerSegmentHeight[finger][segmentToDraw]/2, fingerSegmentWidth [finger][segmentToDraw]/2 ); if (segmentToDraw<2) { translate(fingerSegmentLength[finger][segmentToDraw],0,0); drawFinger(finger, handSide, segmentToDraw+1); translate(-fingerSegmentLength[finger][segmentToDraw],0,0); } rotateZ(-leftHandFingerVertAngle[finger][segmentToDraw]); } /* draw a elliptic cylinder or finger segment */ void drawCylinder(float lengthX, float heigthY, float wideZ) { /* side strip */ beginShape(QUAD_STRIP); for (int i=0; i<9+1; i++) { vertex(0, heigthY*sin(i*PI*2/9), wideZ*cos(i*PI*2/9)); vertex(lengthX, heigthY*sin(i*PI*2/9), wideZ*cos(i*PI*2/9)); } endShape(); } /*************************** draw keys * / void drawBlackKey() { /* sides and bottom */ beginShape(QUAD_STRIP); vertex( 0, 32, 2); vertex( 89, 32, 2); vertex( 0, 20, 0); vertex( 99, 20, 0); vertex( 0, 0, 0); vertex( 99, 0, 0); vertex( 0, 0, 12); vertex( 99, 0, 12); vertex( 0, 20, 12); vertex( 99, 20, 12); vertex( 0, 32, 10); vertex( 89, 32, 10); endShape(); /* back, top and front */ beginShape(QUAD_STRIP); vertex( 0, 0, 0); vertex( 0, 0, 12); vertex( 0, 20, 0); vertex( 0, 20, 12); vertex( 0, 32, 2); vertex( 0, 32, 10); vertex( 89, 32, 2); vertex( 89, 32, 10); vertex( 99, 20, 0); vertex( 99, 20, 12); vertex( 99, 0, 0); vertex( 99, 0, 12); endShape(); } /* draw final C white key, without C# */ void drawFinalC() { /* left side, bottom, right side */ beginShape(QUAD_STRIP); vertex( 0, 20, 0); vertex(150, 20, 0); vertex( 0, 0, 0); vertex(150, 0, 0); vertex( 0, 0, 21); vertex(150, 0, 21); vertex( 0, 20, 21); vertex(150, 20, 21); endShape(); /* back, top and front */ beginShape(QUAD_STRIP); vertex( 0, 0, 0); vertex( 0, 0, 21); vertex( 0, 20, 0); vertex( 0, 20, 21); vertex(150, 20, 0); vertex(150, 20, 21); vertex(150, 0, 0); vertex(150, 0, 21); endShape(); } /* draw C or F white key */ void drawCF(float z3) { /* side strip */ beginShape(QUAD_STRIP); vertex( 0, 0, 0); vertex( 0, 20, 0); vertex(150, 0, 0); vertex(150, 20, 0); vertex(150, 0, 21); vertex(150, 20, 21); vertex(100, 0, 21); vertex(100, 20, 21); vertex(100, 0, 21-z3); vertex(100, 20, 21-z3); vertex( 0, 0, 21-z3); vertex( 0, 20, 21-z3); vertex( 0, 0, 0); vertex( 0, 20, 0); endShape(); /* bottom */ beginShape(TRIANGLE_FAN); vertex(150, 0, 0); vertex( 0, 0, 0); vertex( 0, 0, 21-z3); vertex(100, 0, 21-z3); vertex(100, 0, 21); vertex(150, 0, 21); endShape(); /* top */ beginShape(TRIANGLE_FAN); vertex(150, 20, 0); vertex( 0, 20, 0); vertex( 0, 20, 21-z3); vertex(100, 20, 21-z3); vertex(100, 20, 21); vertex(150, 20, 21); endShape(); } /* draw B or E white key */ void drawBE(float z4) { /* side strip */ beginShape(QUAD_STRIP); vertex( 0, 0, z4); vertex( 0, 20, z4); vertex(100, 0, z4); vertex(100, 20, z4); vertex(100, 0, 0); vertex(100, 20, 0); vertex(150, 0, 0); vertex(150, 20, 0); vertex(150, 0, 21); vertex(150, 20, 21); vertex( 0, 0, 21); vertex( 0, 20, 21); vertex( 0, 0, z4); vertex( 0, 20, z4); endShape(); /* bottom */ beginShape(TRIANGLE_FAN); vertex(150, 0, 21); vertex(150, 0, 0); vertex(100, 0, 0); vertex(100, 0, z4); vertex( 0, 0, z4); vertex( 0, 0, 21); endShape(); /* top */ beginShape(TRIANGLE_FAN); vertex(150, 20, 21); vertex(150, 20, 0); vertex(100, 20, 0); vertex(100, 20, z4); vertex( 0, 20, z4); vertex( 0, 20, 21); endShape(); } /* draw D, G or A white key */ void drawDGA(float z5, float z6) { /* side strip */ beginShape(QUAD_STRIP); vertex( 0, 0, z5); vertex( 0, 20, z5); vertex(100, 0, z5); vertex(100, 20, z5); vertex(100, 0, 0); vertex(100, 20, 0); vertex(150, 0, 0); vertex(150, 20, 0); vertex(150, 0, 21); vertex(150, 20, 21); vertex(100, 0, 21); vertex(100, 20, 21); vertex(100, 0, 21-z6); vertex(100, 20, 21-z6); vertex( 0, 0, 21-z6); vertex( 0, 20, 21-z6); vertex( 0, 0, z5); vertex( 0, 20, z5); endShape(); /* bottom */ beginShape(TRIANGLE_FAN); vertex(100, 0, z5); vertex(100, 0, 0); vertex(150, 0, 0); vertex(150, 0, 21); vertex(100, 0, 21); vertex(100, 0, 21-z6); vertex( 0, 0, 21-z6); vertex( 0, 0, z5); endShape(); /* top */ beginShape(TRIANGLE_FAN); vertex(100, 20, z5); vertex(100, 20, 0); vertex(150, 20, 0); vertex(150, 20, 21); vertex(100, 20, 21); vertex(100, 20, 21-z6); vertex( 0, 20, 21-z6); vertex( 0, 20, z5); endShape(); } /************************************************************** mouse operation / /* getting near or far with mouse wheel */ void mouseWheel(int delta) { fov+=delta/10.0; } /* modify vision angle when pushing mouse button */ void mouseDragged() { if(locked) { difX = mouseX-baseX; difY = mouseY-baseY; if (mouseButton==LEFT) { angleX=previousAngleX+(float)(difX)/(float)width*PI; angleY=previousAngleY+(float)(difY)/(float)height*PI; } else if (mouseButton==RIGHT) { positionX=previousPositionX+difX; positionY=previousPositionY+difY; } } } void mouseReleased() { locked = false; baseX = mouseX; baseY = mouseY; } void mousePressed() { if (locked == false) { locked = true; baseX=mouseX; baseY=mouseY; previousAngleX=angleX; previousAngleY=angleY; previousPositionX=positionX; previousPositionY=positionY; } } /********************* MIDI file loading * / MidiSystem midiSystem; Sequencer midiSequencer; String[] Notes = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; class Note { float m_fTime; String m_sNote; /* relative note */ int m_iOctave; /* note octave */ int m_iFullNote; /* full note */ long m_lTick; /* message tick */ int m_iMessage; /* message type */ } class Song { Song(String sName) { m_sName = sName; } String m_sName = ""; ArrayList m_Notes = new ArrayList(); } int g_iTicksPerBeat = 96; ArrayList loadMidi(String fileName, float fBPM) { String sketchPath = this.sketchPath; println(sketchPath); File midiFile = new File(sketchPath + "/" + fileName); println(midiFile.getAbsolutePath()); if(!midiFile.exists() || midiFile.isDirectory() || !midiFile.canRead()) { println("Error reading file"); } Sequence midiSequence = null; try { midiSequence = midiSystem.getSequence(midiFile); } catch (Exception e) { e.printStackTrace(); } if (midiSequence == null) { return null; } Track[] midiTracks = midiSequence.getTracks(); Track midiTrack = midiTracks[1]; int iNumEvents = midiTrack.size(); ArrayList myNotes = new ArrayList(); String noteOnOffMessage; for (int i=0; i

Source code: modelosteclas_v0_3_6

Built with Processing