CSC 220.010CPVL - Object Oriented Multimedia Programming, Fall 2023, Old Main 158.

Assignment 3 is due by 11:59 PM on Thursday November 9 via D2L.

Read the October 24 doc and listen to the October 24 Zoom recording for changes to STUDENT 3.
 

Added 10/30 in response to student questions:
    All of the instructions are in the STUDENT tab along with most​ of the work.
    There are instructions in Student for adding a constructor call loop in setup() and
        changing a couple Globey functions. I recommend testing after each STUDENT step.

I prefer that you zip the entire folder including all image files and your .pde files into a regular .zip file and turn that in, NOT a .7z or some other format. You may be able to turn in a directory. If you cannot do either, then turn in individual files named CSC220F23Assn3_MIDI.pde, MIDI.pde, STUDENT.pde, Parson.pde and Globey.pde, along with any image files in your project. Please removed spaces from the image file names. I will deduct 10 points for .pde files with different names from these or image files containing spaces, because unpacking (40 students X 7 or more) = 280 files with rando names would take me all day. Thanks!

We are going to do a variant of last years MIDI assignment of exploding HayBales that uses imploding Globeys.

Here is a link to a video showing my solution. Here is the Javadoc for javax.sound.midi.ShortMessage,
    and for the MIDI Fanatic documentation, see "The MIDI Specification" and "General MIDI (GM)".
Here is the code for ConcentricCirclesIntervals that you can use to explore:
    PROGRAM_CHANGE (left and right arrows) to try out instrument patches.
    CONTROL_CHANGE (cN gives you controller N and fL sets its level L from 0 to 127).

STEP 1. Load the linked main code tab into a new Processing sketch and save it as sketch CSC220F23Assn3_MIDI.

STEP 2. Create a new named tab in the Processing IDE and paste the tab's starting code into it, saving after each step.

    Tab MIDI
    Tab STUDENT
    Tab Parson
    Tab Globey
    Save my TriTexture.jpg into your sketch directory for texturing my custom PShape.
        You do not have a custom PShape requirement in this assignment.

STEP 3. Follow these instructions in the STUDENT tab.
    Some require edits in the main
CSC220F23Assn3_MIDI and Globey tabs, but the instructions are in STUDENT.
    Test after every STUDENT step. Do not try to code everything and then test.

From the STUDENT tab comments:

// All STUDENT instructions are in this tab. TEST AFTER EACH STUDENT STEP!!!
// STUDENT AVATAR & any extra variables for it go in STUDENT tab unless otherwise noted.
// This is due via D2L Assignment 3 at the end of Thursday November 9.

// STUDENT 1 (10%) Copy your Avatar class (nothing else) into this tab and add constructor
// calls in the setup() function in the main tab within this loop.
  //for (int i = avatars.length/2-2 ; i < avatars.length ; i++) {
  //    // STUDENT 1 construct your avatars in this loop.
  //}
// Test that your avatar displays and moves properly before STEP 2.

// STUDENT 2 (10%) The constructor parameters should not need to change from Assignment 2.
// There are no new arguments. The MIDI instrument, channel, and other MIDI properties are
// defined inside my Professor class and will be inside your class after it displays.
// Class Professor is in tab Parson. Here are the MIDI data fields at the top of the class.
// You can come up with only one OR more than 1 instrument for your avatar.

//int ProfessorScaleStep = 0 ;
//int ProfessorTimeDelaySlot = 0 ;
//class Professor extends CollisionDetector {
//  /* The data fields store the state of the Avatar. */
//  protected int legdist = 0 ; // You can initialize to a constant here.
//  int channel = 0 ; 
// Your Avatar MUST use channel 10.
//  int mypitch = 0 ;
//  float mydelay = 0 ;
//  float [] delaySeconds = {.1, .15, .25}; IGNORE THE DELAY
//  I stopped using the delay and switched to making sound upon collisions.
//  int instrument = 106 ; // http://midi.teragonaudio.com/tutr/gm.htm

// and in the constructor code:

    //int [] myscale = scales[2];  // major scale
    //mypitch = myscale[ProfessorScaleStep] + 48  ;
    //// major scale, next note in scale, halfway up keyboard
    //ProfessorScaleStep = (ProfessorScaleStep + 1) % myscale.length ;
    //mydelay = delaySeconds[ProfessorTimeDelaySlot];   STUDENT IGNORE DELAY
    //instrument += ProfessorTimeDelaySlot ;
    // You can come up with only one OR more than 1 instrument for your avatar.
    //ProfessorTimeDelaySlot = (ProfessorTimeDelaySlot + 1) % delaySeconds.length ;
   
// I used two global variables ProfessorScaleStep and ProfessorTimeDelaySlot defined
// just outside the class to vary the note (mypitch) instrument, and timing
// (mydelay) for each newly constructed Professor object. Normally a Java "class static"
// variable inside the class would have only 1 value shred by all objects of that class,
// but the Processing framework disallows class static variables for framework
// implementation reasons, so I put them outside the class.

// Your Avatar MUST use channel 10 (I am using 0 and 1 and reserving the others),
// a range of instruments away from my 106 and its neighbors (see
// // http://midi.teragonaudio.com/tutr/gm.htm ) or explore with ConcentricCirclesInterval sketch.

// STUDENT 3 (50%) All of my Professor's MIDI function calls are inside its move() function.
// Note that detectCollisions() now returns a java.util.Set object containing
// all of the colliding Avatar objects, if any, including bales of Globey objects.
// Use this code from detectCollisions()'s return value to sonify collisions.
// Use your own new or added CONTROL_CHANGEs or other MIDI messages, e.g., 2 notes at a time.

    //Set<Avatar> colliders = detectCollisions();
    //if (colliders.size() > 0) {
    //  sendMIDI(ShortMessage.PROGRAM_CHANGE, channel, instrument, 0);
    //  int volumeAdjust = 7 ;
    //  sendMIDI(ShortMessage.CONTROL_CHANGE, channel, volumeAdjust, 127);
    //  int balanceControl = 8 ;
    //  int balanceLocation = int(constrain(map(myx, 0, width, 0, 127),0,127));  // 64 is centered in stereo field
    //  sendMIDI(ShortMessage.CONTROL_CHANGE, channel, balanceControl, balanceLocation);
    //  sendMIDI(ShortMessage.NOTE_ON, channel, mypitch, 127);
    //  // See http://midi.teragonaudio.com/tech/midispec.htm
    //}
    //for (Avatar other : colliders) {
    //  float probability = random(0, 100);
    //  if (probability < 60) { // mow 60% of them
    //    other.mow();
    //  }
    //}
   
    // 3a. CHANGE the probability if mow()ing a Globey from 60% to something else. Play around with values.
    // 3b. Add a CONTROL_CHANGE MIDI effect that you can hear, see
    // http://midi.teragonaudio.com/tech/midispec.htm and experiment with ConcentricCirclesInterval sketch.
    // Take a listen to modulation wheel (controller 1) and chorus (controller 93) Note that unlike the
    // instrument numbers, these start at 0, i.e., mod wheel is 1, bank select is 0. Make it a mapped
    // function of y location relative to [0, height-1] or another avatar varying variable.
    // 3c. Add a second CONTROL_CHANGE MIDI effect that you can hear. Make it a mapped function
    // of z location relative to minimumZ and maximumZ or anotehr avara varying variable.
   
 // STUDENT 4 (30%) Make an imploding Globey similar to a video I will post.
 // Here is what I did for the video:
 // 4a. changed this in Globey's mow() function:
  // void mow() {
  //  avatars[avatarsIndex] = null ;
  //  GlobeyCount = constrain(GlobeyCount-1, 0, GlobeyCount);
  //  // println("DEBUG GlobeyCount GlobeyMAX", GlobeyCount, GlobeyMax);
  //  if (GlobeyCount == 0) {
  //    sendMIDI(ShortMessage.NOTE_OFF, channel, pitch, 0);
  //  }
  //}
  // TO THIS:
  //void mow() {
  //  mowCounter += 1 ;
  //}
  // I initialized the "int mowCounter = 0" code in the top of class Globey.
 
 
  // 4b. The move() function in Globey now does the following pseudo-code:
  //  If the mowCounter is greater than 0
  //      Add 1 to mowCounter
  //      If mowScaleXYZ is a value very close to 0.0 (it may never reach 0).
  //        Do the steps previously in Globey.mow(), i.e., null this object's avatars[] entry
  //        and send the NOTE_OFF if the decrement GlobeyCount <= 0.
 
  //  4c. In Globey.display, immediately after translate, if the mowCounter is greater than 0,
  //      scale(mowScaleXYZ), where mowScaleXYZ is initialized to 1.0 in the constructor.
  //      Then, still within the "if the mowCounter is greater than 0" scope,
  //      decrement mowScaleXYZ by some tiny but vsible amount. This will shrink the sphere.
  //      See step 4b above for when mowScaleXYZ approaches 0. I played around with shrinkage
  //      decerements and the boundary for sphere disappearance in step 4b.
  //      THE SCALE MUST MAKE THE GLOBEY SHRINK ***SLOWLY***, NOT EXPLODE!!!
  //      Basically, you want the scale in this step to approach 0 slowly, and when it is
  //      close to 0 (invisible in at least 1 dimension), do the step 4.b removal with NOTE_OFF.
  //      You should leave the bounding box for the sphere full size as it shrinks,
  //      in order to keep a higher probability of collision.
 
  // 4d. In the Globey tab change this line of code in makeglobePShape():
  //     globePImage = loadImage("clover.png");
  //     to load a .jpg or .png supplied by you. Make sure to include that image
  //     file in your project directory and turn it in to D2L with ALL YOUR CODE .pde files,
  //     preferrabbly as a regular .zip file.
  //     Then do the following as given in makeglobePShape():
  // STUDENT set the texture on this PShape to globePImage.
  // See calls to setTexture(t) in
  // https://faculty.kutztown.edu/parson/fall2022/CSC220FA2022DemoSome3D.txt
  // If you use an image off the web with no copyright restriction, cite its URL in a command.